IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Test, UAT, Prod : Comment distribuer les différentes versions de vos applications Access ?

Ce tutoriel a pour objectif de vous proposer une solution de distribution de vos applications selon les différentes étapes de leur développement dans les trois environnements connus : Test, Recette (UAT) ou Prod (Production).

Commentez Donner une note à l´article (0)

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

1. Avant-propos

Ce tutoriel a pour objectif de vous proposer une solution de distribution de vos applications selon les différentes étapes de leur développement dans les trois environnements connus : Test, Recette (UAT)1 ou Prod (Production).

Pour ce faire, vous devez être à l'aise avec Microsoft Access et avoir déjà distribué des applications (avec ou sans Runtime) dans un contexte professionnel.

Ce tutoriel est né de l'idée conjoncturelle d'une situation moult fois rencontrée dans le cadre de mes activités professionnelles.
En effet, chaque fois que l'occasion se présente, je partage des bouts de mes applications qui ouvrent selon moi une voie intéressante pour vous, en tout cas, c'est l'objectif.

Contexte professionnel

Cet article concerne une situation professionnelle réelle dans laquelle je vous propose de mettre en pratique une solution idéale pour le déploiement de vos applications dans votre propre contexte professionnel.

Dans l'exemple qui va suivre, j'ai intentionnellement supprimé des champs en réduisant leur nombre dans la table, et ce, dans l'esprit de pouvoir simplifier la réalisation…
Il vous appartiendra d'ajuster le nombre de champs nécessaires à ajouter pour satisfaire vos besoins…

1-2. Niveau

Ce tutoriel s'adresse plus particulièrement aux personnes qui possèdent déjà des bonnes notions en matière de développement d'applications à vocation professionnelles et ayant déjà été confrontés à la problématique conjointe du déploiement et de l'évolution de leurs applications.

Pour la distribution d'application, vous pouvez vous référer au tutoriel sur le Déploiement et empaquetage d'applications Access 2010

1-3. Contact

Pour tous renseignements complémentaires, veuillez me contacter directement (Argyronet) par MP.
Si votre question est, disons publique, merci de poster un message dans le forum afin d'en faire partager le contenu...

1-4. Images

Les images représentées ont été créées à l'aide d'un logiciel spécifique.

Elles ne sont pas disponibles en téléchargement.

1-5. Langue

Les commentaires du code de ce tutoriel sont rédigés en français.

Les noms des contrôles et les procédures de code sont, quant à eux, en anglais

1-6. Versions

Ce tutoriel est applicable pour vos applications qui ont été développées à partir de la version 97 de Microsoft Access (avec quelques adaptations à prévoir pour la version 97).

2. Présentation du projet

Ce tutoriel a pour objectif de vous apprendre à mettre en place une solution de déploiement simplissime pour que vous puissiez concevoir une version de Test puis mettre à la disposition des utilisateurs une version UAT1 puis une version de Production encapsulée ou non dans la même application.

On considère alors que la notion de Test, UAT1 ou Production concerne aussi bien la base de données dorsale que, l'application frontale.

En effet, un ou plusieurs utilisateurs vont être chargés de tester la nouvelle version de l'application que vous leur avez mis à disposition mais selon l'état de celle-ci, devoir affonter les éventuelles corrections ou évolutions au fur et à mesure que son développement progresse.

2-1. A qui s'adresse ce tutoriel ?

Ce tutoriel est destiné aux développeurs dont l'activité est majoritairement axée sur des solutions dites "tactiques" au sein de l'entreprise dans laquelle ils travaillent.

Le niveau de connaissance requis est élevé, mais c'est surtout la rigueur de la qualité du côté rédactionnel du code qui fait foi ici.

En effet, vous ne pourrez en quelque sorte mettre en application ce tutoriel que si votre code est structuré avec des imbrications de fonctions et de procédures factorisées en grand nombre.
J'insiste là-dessus dans le sens où si vous êtes amené à modifier la résultante ou le contenu d'une fonction sous prétexte qu'elle doive subir des évolutions alors que celle-ci est déjà en fonctionnement dans la version actuellement en place chez vos utilisateurs, il sera plus aisé d'implémenter un bloc conditionnel vérifiant le contexte de l'environnement et d'exécuter le code en conséquence.

2-2. Que sont et comment sont utilisés ces environnements ?

Les environnements sont au nombre de trois

L'environnement de TEST

Il s'agit en fait de votre propre environnement durant lequel, vous procédez comme son nom l'indique à des tests au moment de l'exécution du code pour contrôler le bon fonctionnement de l'application avant la mise en Recette.
J'aurais pu tout aussi bien dans cet esprit le nommer Environnement de développement.

L'environnement de RECETTE

Il s'agit de l'environnement qui se traduit par la phase d'acceptation des utilisateurs avant la mise en production effective.
En fait, une fois vos tests concluants, vous proposez une mise en recette. Vous attendez alors les éventuelles remontées des utilisateurs qui peuvent inclure aussi bien des bugs que des dysfonctionnements qui émanent en toute logique des dernières modifications apportées sauf biensûr s'il s'agit d'une première livraison.

L'environnement de PROD

Il s'agit de la version finalisée et opérationnelle de votre application exempt de bugs et de mauvais comportements.
Toutes les corrections apportées après la mise en recette ont bien entendu été soumises à une nouvelle mise en recette une fois les tests concluants.

2-3. Principe de fonctionnement

Le principe de fonctionnement est simple et complexe à la fois...

Simple parce que la solution mise en place pour le contrôle de l'environnement reste rudimentaire.

Complexe parce qu'il vous appartiendra d'adapter votre code de façon bien ficelée pour que ce complément s'adapte à bon escient. Il est en effet délicat pour moi de vous proposer quelque chose d'absolument générique prêt à être encapsuler dans votre code du fait de la diversité des méthodes de développement employée par tout un chacun.

En effet, si vous souhaitez mettre en oeuvre ce tutoriel, l'idéal est de mettre en place les procédure de détection de l'environnement dès la création du projet.

Dans le cas d'un projet existant en revanche, la mise en place des routines de détection des environnements sera d'autant plus accessible que votre code est bien rédigé et bien entendu, l'inverse lorsque que vous avez face à vous un code écrit à la mode plat de spaghettis.

A titre d'exemple (Historique) :

Le dernier projet que j'ai mis en œuvre importait selon de multiples conditions définies dans des tables de paramètres, un lot de fichiers Excel (avec de nombreux onglets d'une part et des structures différentes d'autre part) générés par l'ERP PeopleSoft® avec tous les inconvénients que cela relève...

Une fois importés, les onglets cibles étaient dispatchés ou fusionnés selon le cas dans différentes tables.
Un long traitement de nettoyage, de mise en forme, de contrôle de validité, de corrections automatiques, de notifications automatiques dans un fichier LOG était généré durant le cycle d'exécution. Ce cycle allait, selon l'environnement choisi, chercher les fichiers dans les dossiers spécifiés par la table définissant l'environnement.
Une fois toutes les données importées et mises en forme, donc présentées dans un formulaire de présentation final, l'utilisateur pouvait effectuer des exports vers Excel pour l'ensemble des données, cette fois adaptées et corrigées manuellement par lui-même. L'export construisait dynamiquement autant de classeurs que nécessaire et les répartissaient dans des dossiers cibles ainsi que dans des dossiers Document Library5 d'un portail SharePoint®.

En résumé, l'utilisateur pouvait alors conserver son travail de Prod en cours et tester une nouvelle version de l'application en UAT1 dans le même temps, et ce, sans affecter les données de la base de données de production, ni les classeurs générés déjà dans les dossiers ou dans le portail SharePoint.

****************************************************************************************************
Le souci majeur était que le cahier des charges de ce projet était de base incomplet et confus mais le temps pressait et j'avais un temps limité par mon estimation pour le réaliser. Constatant au fur et à mesure que des problèmes naissaient dès les premières mises en recette, j'ai eu l'idée de mettre en place ce concept afin de me garantir plus de souplesse au fur et à mesure que les corrections étaient appliquées et que les nouvelles demandes tombaient. Entre les premières lignes de code écrites et la mise en production finale, pas moins de cent quarante modifications, évolutions ou nouvelles demandes ont été traitées.
****************************************************************************************************

L'objectif est donc que vous puissiez transposer cet exemple de contexte professionnel de ce tutoriel pour vos propres projets.

Déroulement synoptique

La représentation schématique du programme de ce projet exécutait telle ou telle procédure ou telle ou telle fonction en prenant les informations requises dans la table des environnements afin de définir le comportement de l'application et de travailler sur la base de données dorsale en rapport...

Image non disponible
Synoptique du projet



Je vous propose donc de mettre en application l'exemple de ce tutoriel sachant bien évidemment qu'une grande partie du contenu du formulaire à créer autant que le sujet abordé ici ne s'accorderont pas forcément avec votre projet.
Seule l'idée est à retenir du fait que dans la plus grande partie des cas rencontrés en entreprise, les applications Access ont très souvent à importer et / ou exporter des fichiers...

Dans ce synoptique, j'ai illustré un contexte similaire à mon projet dont l'objectif était d'importer des fichiers plats (quels qu'ils soient) et de générer des rapport statistiques, cas typique d'une application lambda...

On suppose ici détenir des fichiers plats mis à disposition dans un certain dossier défini en fonction de l'environnement dans lequel l'application est configurée.

Un processus d'importation, de traitement, de stockage (partiel ou total) et de formatage suivi éventuellement d'autres procédés [...] est alors exécuté, et ce dans la base de données cible dont les tables sont censées être liées.

Il se passe un certain nombre de choses dans l'application une fois tous ces processus exécutés et l'utilisateur peut alors "jouer" avec les interfaces de consultation, de mise à jour et de gestion de l'application en général.
Lorsque ce dernier le souhaite, il peut générer des fichiers de sortie, classeurs Excel ou fichiers plats ou d'autres exportations diverses et variées. La génération des fichiers s'exécutera et déposera les fichiers dans les dossiers cibles définis encore une fois selon l'environnement.

En parallèle à cela, le projet veut que les fichiers générés soient déposés sur un site SharePoint (donc dans des dossiers de type Document Library5).
Je spécifie cela pour la forme car cela n'apporte rien au projet si ce n'est vous apprendre à coller une adresse dans une zone de texte et contrôler en parallèle, sa validité.

Les tables associées
Dans ce projet, je pars du principe sans les aborder que des tables existent pour tous les traitements notamment celles contenant les noms des fichiers à importer.

2-4. Mise en oeuvre du projet

Pour mettre en œuvre ce tutoriel, votre application devra être une scindée en une structure Frontal/Dorsal.

Vous devrez alors vous appuyer sur le synoptique et donc copier 3 exemplaires (si nécessaire) de la base de données dorsale initiale et donc finalisée. Considérez alors la notion de version de la base de données afin de garantir le bon fonctionnement.

Vous pouvez par exemple créer une table tblDBVersion dans laquelle un champ spécifie la version en vigueur, un autre la date correspondant à la dernière évolution et un autre qui résume, pourquoi pas, les derniers changements mis en application. Pour ce dernier point, il est fortement recommandé de noter dans un document annexe tous les changements relatifs aux tables :

  • Ajouts de champs
  • Changements de type de champ
  • Changements de taille de champ
  • Changements de nom de champ
  • Etc...

Le but consiste à répercuter ces modifications sur la base de données UAT1 puis sur celle de production. au final.
Parallèlement à cela, notez que pour tous types ou tailles des champs existants qui auraient été modifiés, une répercussion non négligeable est à prendre en considération côté risque de perte de données. Il est alors recommandé de procéder à des sauvegardes avant de procéder à de telles opérations...

2-5. Création de la table des environnements

Le projet exposé ici ne comporte qu'une seule table et contient donc les trois environnements de base et un réservé au développeur.
En réalité, les deux environnement de TEST sont réservés au développeur puisque c'est sous cet environnement que justement, les tests sont réalisés avant la mise en Recette. Dans cette section, j'explique la raison de la présence du champ ReservedToDeveloper.

Image non disponible



La table est composée de treize champs :

Pour mettre en application ce tutoriel, vous devez créer la table des environnements. Il vous appartient de mettre autant de champs que nécessaire selon l'objectif de votre projet.
Ici, dans notre exemple, j'ai ajouté les champs qui sont en rapport direct avec le projet d'importation et d'exportation de fichiers même si ce n'est qu'une partie du projet en lui-même. Le travail expliqué ici n'a pas forcément de rapport avec votre projet mais si vous lisez ces lignes, c'est que vous y portez tout de même un intérêt.
Dans ce contexte, nous avons besoin des champs définissant tous les éléments requis pour effectuer ces opérations.

Le tableau ci-dessous propose la liste des champs et leur description.

Nom Type Taille Description
EnvironmentID Entier long 4 Identifiant de l'environnement
IsInUse Oui/Non 1 True si l'environnement est actif
ReservedToDeveloper Oui/Non 1 True si réservé au développeur
EnvironmentName Texte 64 Nom donné à l'environnement
LinkedDatabaseName Texte 64 Nom de la base de données source
LinkedDatabasePath Texte 255 Chemin de la base de données source
WorkingPath Texte 255 Dossier de travail
DataFileFolder Texte 64 Dossier source où sont puisés les fichiers de données
OutputFolder Texte 64 Dossier où sont stockés les fichiers exportés
OutputFilename Texte 64 Nom du fichier final de sortie (par exemple Excel)
DateFormat Texte 32 Format de la date dans le nom du fichier par exemple yyyymmdd
IsUSFormat Oui/Non 1 True si le format de la date du nom de fichier est américain
ArchiveFolder Texte 64 Dossier où sont archivés les fichiers de données sources
SharePointEnabled Oui/Non 1 True si le site SharePoint est actif
SharePointAddress Texte 255 Adresse du site SharePoint

Remarque:

Les trois premiers champs représentent la clé primaire.
En effet, lorsque l'on rend un environnement actif, il faut considérer le trio EnvironmentID, IsInUse et ReservedToDeveloper.
Dans le tableau ci-dessus, ces champs sont en gras.
Il n'est en effet pas possible alors de créer un environnement portant le même ID et le même nom qu'il soit réservé au développeur ou non.

Rappel Par principe, ne mettez jamais d'accents ni d'espaces dans le nom des champs. C'est pourquoi je nomme toujours mes champs en anglais.
Pour plus d'information sur les conventions de nommage des objets, vous pouvez lire ce tutoriel.

Vous enregistrez la table par exemple sous le nom tblEnvironment.

2-5-1. Vue de la table en mode création

La table en mode création une fois les champs créés et la table enregistrée.

Image non disponible
Structure de la table

2-6. Remplissage de la table

Pour pouvoir utiliser la notion de gestion d'environnements dans votre projet, vous devez créer/ajouter manuellement les trois environnements.

Cela se résume à renseigner seulement les quatre premiers champs, car les autres seront ou pourront être ajoutrés depuis le formulaire que vous allez créer et dans lequel des boutons de commande vous permettront de sélectionner la base de dnnées dorsale, le chemin de travail et les dossiers cibles pour chacun des environnements. Cela évitera les erreurs de saisie, chose courante lorsque des données sont entrées directement dans une table.

2-6-1. Vue de la table en mode feuille de données

La table en mode feuille de données une fois les environnements saisis.

Image non disponible
La table en mode feuille de données

Comme vous pouvez le constater, j'ai fait en sorte que la table ne soit pas intégralement remplie...

=> ce tutoriel et son application ne prévoient pas que la table soit dépourvue de spécifications d'environnement.
Ces derniers n'existent qu'à travers leurs ID respectifs. Aucun processus ne se charge de la saisie des ID dans cette table d'une part, et que de toute façon, cette table et le formulaire qui l'accompagne sont réservés au développeur ou à l'équipe informatique d'autre part...
Vous saisirez donc les 3 environnements, ID + Nom directement depuis la table, ces données étant figées...

3. Création du formulaire

La réalisation présentée ici est purement suggestive... Ici, elle est en tout cas très fidèle à l'existant original.

Je pars donc du principe que vous allez vous en inspirer.

Vous créez donc un nouveau formulaire vide (Mode création) sans en-tête / pied de formulaire dans lequel vous aurez à insérer autant de contrôles qu'il en existe dans la table source parmi zones de texte (textbox), zones de liste modifiable (ComboBox), étiquettes (Label) et boutons de commande (CommandButton)...

Il existe cependant deux contrôles cachés :
- qui pour l'un, représente l'identifiant de l'environnement (IDEnvironment)
- et pour l'autre qui représente le nom de l'environnement (EnvironmentName)

Ces deux zones de texte indépendantes restent masquées et seront exploitées par le programme au moment de l'utilisation.

Image non disponible
Le formulaire en mode création (fini)

Sans rentrer dans le détail sur l'aspect graphique du formulaire, point pour lequel je vous laisse libre cours à votre imagination, je vais aborder sa conception au niveau des contrôles propres à son utilisation.
Comme vous pouvez le voir, j'ai utilisé un grand nombre d'objets dessinés et notamment des Lignes (Shape) dans le seul but de faire joli d'une part et de séparer les valeurs qui ont un point commun d'autre part.

Pourquoi les contrôles sont indépendants

Tous les contrôles du formulaire présentés ici sont indépendants. En d'autres termes, ils ne n'ont pas de propriété ControlSource (Origine Source).
C'est ainsi que je procède dans la majorité des applications que je développe où j'ai pris l'habitude de concevoir des formulaires indépendants de la source. Même si cela est un procédé plus complexe à mettre en oeuvre, il est évident, et c'est mon opinion, que cela rend plus souple leur utilisation.

Si vous préférez lier les contrôles à la table source précédemment créée, il n'y a pas d'objection. Toutefois, le code présenté ici n'aborde pas ce cas de figure et il vous appartiendra de l'adapter en conséquence et de gérer tous les événements inhérents à ce mode de réalisation de formulaires Access.



De haut en bas :

0°)   Tout d'abord, dans un coin du formulaire, dessinez deux zones de texte cachées (Visible=Non) que vous nommez respectivement EnvironmentID et EnvironmentName.
Ces deux contrôles sont utilisés par le code pour identifier l'environnement par son ID et son nom en vue de les appliquer à d'autres contrôles ou a des messages utltérieurement...

1°)   Vous dessinerez ensuite en haut et à gauche, une zone de liste déroulante qui servira a proposer les différents environnements.
Cette liste voit sa propriété RowSource (Contenu) basée sur la chaîne SQL suivante :

Requête de la liste déroulante
Sélectionnez
SELECT EnvironmentID, EnvironmentName
FROM tblProcessEnvironment
WHERE ReservedToDeveloper=False;

Il est recommandé de sauvegarder la requête sous un nom approprié plutôt que de laisser la chaîne SQL dans la zone de propriétés, soit par exemple qry_cboEnvironmentList.
Vous nommerez par exemple cette liste déroulante cboCurEnvironment.

Dans l'idéal, et du fait que j'ai prévu une constante qui défini la valeur du champ ReservedToDeveloper, il est effectivement préférable de faire en sorte que cette liste déroulante possède une source de contrôle dynamique et établie en fonction de la valeur de cette constante.

L'événement Form_Load() vu plus bas sera aussi proposé avec cette condition...

Requête dynamique
Sélectionnez
Dim SQLEnvList As String
	
	SQLEnvList="SELECT EnvironmentID, EnvironmentName FROM tblProcessEnvironment "
	SQLEnvList= SQLEnvList & "WHERE ReservedToDeveloper=" & RESERVED_TO_DEVELOPER & ";"

2°)   A droite de cette zone de liste, vous dessinerez une case à cocher qui va permettre à l'utilisateur ou à vous-même de choisir si vous voulez définir l'environnement affiché comme étant actif. L'événement Timer du formulaire changera la couleur de sa légende et celle du cadre qui l'entoure selon que l'environnement choisi dans la liste est actif ou non.

Cette case à cocher sollicitera la valeur du champ IsInUse dans la table au moment de l'exécution du code.
Pour plus de commodité, vous nommerez cette case à cocher avec le même nom que le champ qu'elle alimentera (IsInUse).

L'ensemble des zones de texte

Maintenant, nous allons dessiner 11 zones de texte dont les longueurs varient en fonction de leur contenu.
Chacune de ces zones de texte reçoit une étiquette avec sa description du contenu concerné.
Pour la plupart d'entre elles, un petit bouton est dessiné à droite avec pour illustration, l'action Ouvrir, dont l'icône caractéristique pour les opérations similaires sur les fichiers est affectée à leur propriété Image. Cette illustration fait partie de la liste des bitmaps disponibles directement depuis l'assistant [...] Images de Microsoft Access.

Ces boutons auront pour action d'ouvrir la boîte de dialogue permettant de sélectionner un fichier ou un dossier selon le cas.
Pour chacun d'entre eux, une fonction appropriée sera appelée et retournera une chaîne de caractères qui représentera :
- soit le dossier,
- soit le nom du fichier,
- soit le chemin complet.

3°)   La première zone de texte à dessiner permettra de recevoir le chemin dans lequel se trouve la base de données dorsale.
Vous nommerez cette zone txtDatabasePath et lui affecterez une longueur suffisante, à savoir pratiquement toute la longueur du formulaire.
L'étiquette de cette zone de texte voit sa propriété Caption (légende) affectée avec le texte Chemin de la base de données dorsale :
Le nom de ce contrôle Label est le même que le nom de la zone de texte excepté son préfixe qui permet de l'identifier : lblDatabasePath.
À droite de cette zone de texte, vous dessinerez un petit bouton carré comme évoqué ci-avant. Il portera le nom cmdDatabasePath.
Le code affecté à ce bouton sera abordé un peu plus loin...

4°)   Juste en dessous de ce contrôle, vous dessinerez une autre zone de texte qui contiendra cette fois le nom de la base de données dorsale.
Vous nommerez cette zone txtDatabaseName et lui affecterez une longueur qui représente grossièrement un tiers de la zone de texte représentant le chemin.
L'étiquette de cette zone de texte est porte la légende Nom de la base de données dorsale :
Le nom de ce contrôle Label rappelle le même que le nom de la zone de texte excepté son préfixe : lblDatabaseName.

Juste au-dessous de ces deux zones de texte vous avez la possibilité de dessiner un trait de séparation ainsi que vous le voyez dans l'illustration.

5°)   La zone de texte suivante contiendra cette fois le chemin du dossier de travail. il s'agit en réalité du chemin qui fait office de racine pour tous les sous-dossiers qui vont contenir les différents éléments contenus dans la fenêtre et que nous allons aussi affecter à des différentes zones de texte.
Vous nommerez cette zone txtWorkingPath et lui affecterez la même longueur que celle que vous avez donnée à la zone de texte représentant le chemin de la base de données et ce, dans le but de respecter l'aspect graphique du formulaire.
L'étiquette de cette zone de texte est porte la légende Chemin principal de travail :
Le nom de ce contrôle Label rappelle le même que le nom de la zone de texte excepté son préfixe : lblWorkingPath.
Au même titre que vous l'avez fait pour la zone de texte représentant le chemin de la base de données dorsale, vous dessinerez à droite de celle-ci le même petit bouton carré, le mieux étant bien entendu de faire un copier-coller de celui que vous avez déjà posé.
Il portera le nom cmdWorkingPath et le code qui lui sera affecté sera abordé un peu plus loin...

6°)   Nous continuerons dans la même lancée, la zone de texte suivante contiendra le nom du dossier où sont stockés les fichiers de données sources. C'est effectivement dans ce dossier que seront stockés les fichiers qui contiennent les données importées dans la base.
On peut ici considérer qu'un processus externe dépose les fichiers automatiquement dans ce dossier, mais on peut également envisager de les déposer à la main. Quel que soit le procédé, votre programme est censé lire ce dossier pour vérifier qu'il y a ou non des nouveaux fichiers arrivés. Le cas n'a pas été prévu ici, mais il peut être intéressant de faire en sorte que votre programme ne lise les fichiers qui portent une certaine convention typographique et éventuellement un horodatage.

Dans mon cas personnel, les fichiers à importer portaient toujours le même nom et ils étaient au nombre de quatre, de ce fait, il n'y avait qu'à vérifier leur présence. Du fait de leur nom était identique à chaque fois, le seul moyen de pouvoir faire la différence entre un fichier déjà importé et sa présence effective dans ce dossier consistait de les ouvrir pour en lire le contenu dans une zone spécifique dudit fichier. Ces fichiers étaient des fichiers Excel et la lecture de quelques cellules prédéfinies pour vérifier la période suffisait.

Vous nommerez cette zone txtDataFileFolder et lui affecterez une longueur suffisante pour que le nom de ce fichier soit lisible dans son entier.
L'étiquette de cette zone de texte est nantie de la légende Dossier où sont stockés les fichiers de données source :
Le nom de ce contrôle Label est le même que le nom de la zone de texte excepté son préfixe : lblDataFileFolder.
Juste à côté de cette zone de texte, vous dessinerez ou recopierez le bouton permettant de sélectionner ce dossier. Vous lui attribuerez le même nom que ce pourquoi il est destiné, hormis son préfixe, à savoir cmdDataFileFolder.

7°)  En dessous de cette zone de texte, vous dessinerez une autre zone de texte qui contiendra le nom du dossier où seront stockés les fichiers générés.
Vous nommerez cette zone txtOutputFolder et lui affecterez une longueur suffisante, à savoir pratiquement toute la longueur du formulaire.
L'étiquette de cette zone de texte est nantie de la légende Dossier où sont stockés les fichiers générés :
Le nom de ce contrôle Label est le même que le nom de la zone de texte excepté son préfixe : lblOutputFolder.

Juste à côté de cette zone de texte, vous dessinerez ou recopierez le bouton permettant de sélectionner ce dossier. Vous lui attribuerez le même nom que ce pourquoi il est destiné, hormis son préfixe, à savoir cmdOutputFolder.

8°)  En dessous de ce contrôle, vous dessinerez deux zones de texte qui contiendront respectivement le nom et l'extension du fichier de sortie.
Vous nommerez ces zones txtOutputFilename et txtFileExtension leur affecterez une longueur suffisante, celle de l'extension ne devant contenir que 5 caractères max.
Les étiquettes de ces zones de texte sont nanties de la légende Nom du fichier de sortie : et Extension :
Le nom de ces contrôles Label est le même que le nom de leur zone de texte excepté les préfixes : lblOutputFilename et lblFileExtension.

9°)  En dessous de ces deux contrôles, vous dessinerez une zone de texte qui contiendra le format de sortie pour l'horodatage du fichier et juste à côté de cette zone de texte, une case à cocher qui permet de spécifier que l'on souhaite ou non un format US pour la date.
Vous nommerez cette zone txtDateFormat et lui affecterez une longueur identique à celle du dessus (côté esthétique) et ferez en sorte que la position gauche de la case à cocher soit alignée avec le bord gauche de la zone de texte au-dessus et contenant l'extension. Vous nommerez la case à cocher chkIsUSFormat.
L'étiquette de cette zone de texte est nantie de la légende Format de la date pour le fichier : et celle de la case à cocher Format US
Le nom de ce contrôle Label est le même que le nom de la zone de texte excepté son préfixe : lblDateFormat et lblIsUSFormat pour l'étiquette de la case à cocher

10°)  La zone de texte suivante contiendra le nom du dossier où seront archivés les fichiers sources une fois le traitement terminé... C'est effectivement dans ce dossier que seront stockés les fichiers tels que reçus avant traitement dans leur intégrité.
Vous nommerez cette zone txtArchiveFolder et lui affecterez la même longueur que le contrôle txtDataFileFolder.
L'étiquette de cette zone de texte est nantie de la légende Dossier où sont stockés les fichiers de données source :
Le nom de ce contrôle Label est le même que le nom de la zone de texte excepté son préfixe : lblArchiveFolder.
Juste à côté de cette zone de texte, vous dessinerez ou recopierez le bouton permettant de sélectionner ce dossier. Vous lui attribuerez le même nom que ce pourquoi il est destiné, hormis son préfixe, à savoir cmdArchiveFolder.

11°)  Juste en dessous de ce contrôle, vous pouvez ajouter ainsi que je l'ai fait, un contrôle label qui permet de préciser que les dossiers qui n'existent pas seront créés automatiquement par le programme.
L'étiquette de cette zone de texte est nantie de la légende * Si un de ces dossiers n'existe pas, il sera créé automatiquement par le programme.
Le nom de ce contrôle Label est le même que le nom de la zone de texte excepté son préfixe : lblSubFolderInfo.

12°)  Juste en dessous de ce contrôle, vous dessinerez deux lignes superposées en guise de séparateur. Vous les nommerez line03 et line04

*** FACULTATIF ***

Ainsi que je vous l'avais précisé, le projet initial disposait de zones de saisie de sites SharePoint.
Vous n'êtes en aucun cas tenu d'ajouter ce contrôle et les boutons qui l'accompagnent. C'est juste à titre d'exemple pour fidéliser le projet en question d'une part et vous apprendre à l'envisager si cela vous concernait un jour d'autre part.

13°)  Donc, vous dessinerez une dernière zone de texte qui contiendra l'adresse du site SharePoint où sont déposés les fichiers si cela vous concerne, bien évidemment.
Vous adapterez et l'interface et le code selon le cas.
Vous nommerez cette zone txtSharePointAddress et lui affecterez une longueur suffisante, à savoir pratiquement toute la longueur du formulaire, mais prêterez attention au fait que deux boutons de commandes jouxtent celle-ci. Vous ferez alors en sorte que le bouton le plus à droite ait la même position que celui définissant le choix du chemin principal de travail.
- L'étiquette de cette zone de texte est nantie de la légende Adresse du site SharePoint où sont déposés les fichiers :
- Le nom de ce contrôle Label est le même que le nom de la zone de texte excepté son préfixe : lblSharePointAddress.
- Le premier bouton servant à coller une adresse web porte le nom cmdPasteSPAddress et se voit attribuer l'image du symbole Coller.
- Le second bouton servant à vérifier l'adresse web porte le nom cmdCheckSPAddress et se voit attribuer l'image du symbole représentant une feuille et un globe.

3-1. Affectation des propriétés aux contrôles

Seule la zone de liste reçoit des propriétés initiales...

3-1-2. Les propriétés des autres contrôles

Toutes les propriétés des autres contrôles restent celles par défaut à l'exception :
- des boutons cmdUpdate et cmdValid qui ont leur propriété Activé (Enabled) définie à Non (False) car leur état sera modifié dynamiquement le programme en fonction de ce qui se passe dans le formulaire.
- des zones de texte txtDatabaseName, txtDatabasePath et txtWorkingPath qui voient leur propriété Verrouilé (Locked) définie à Oui (True) puisque le formulaire veut que ces chemins soient inscrits par le biais des fonctions qui permettent de les sélectionner.

3-1-3. Disposition des contrôles

Vous agencerez ce formulaire comme bon vous semble. Celui présenté ici reste comme déjà précisé, une suggestion.

Ordre de tabulation Dans ce formulaire, vous devez affecter à la propriété Arrêt tabulation (TabStop) la valeur Oui (True) pour les contrôles de zone de texte (Textbox), zone de liste (Listbox) ; concernant les deux boutons du bas (CommandButton), cette affectation est facultative qui plus est si vous avez défini leur légende avec une esperluette idoine.
L'ordre sera alors 0 pour la zone de liste, 1 pour la première, zones de texte et ainsi de suite...
Il n'est pas ou peu utile de donner les focus par la touche TAB aux contrôles qui possède un bouton de sélection car il n'y a pas lieu d'intervenir dedans en mode d'utilisation disons standard.


Exécution du formulaire en l'état

Vous pouvez passer en mode formulaire (ou appuyer sur F5) pour voir si votre agencement vous paraît correct.

Cela doit ressembler à :

Image non disponible
Mode formulaire (premier jet)


Vu qu'aucun code n'a été affecté au formulaire et ses contrôles, il est évident que rien n'est rempli dans les zones de texte...

3-1-4. Sauvegarde du formulaire

Il est maintenant temps, si ce n'est déjà fait, de sauvegarder le formulaire.
Pensez au fur et à mesure que vous construisez le formulaire à presser les touches Ctrl+S pour sauver ce que vous avez déjà fait.
Pour cet objet, vous lui attribuerez le nom de frmEnvironmentManagement ou tout autre nom qui vous convient.

3-1-5. Mise en place du code événement

Pour affecter des événements aux différents contrôles, différentes solutions sont possibles...

Tout d'abord...
Pour mettre en place le code événement, il se peut que vos options ne soient pas définies pour attaquer à chaque fois l'éditeur Visual Basic (VBE)...
Aussi, pour rendre plus confortable la rédaction du code, je vous invite à forcer l'usage de procédures événementielles pour chaque événement et éviter l'apparition systématique de cette boîte de dialogue :

Image non disponible



Veuillez activer les options d'Access et cocher la case appropriée dans la rubrique concernée : la rubrique est intitulée :
"Toujours utiliser les procédures événementielles"...

Image non disponible
Option des événements


Passer dans l'éditeur Visual Basic

À l'extérieur du formulaire tout en restant dans la fenêtre de celui-ci, effectuez un clic droit et choisissez la commande : "Créer code événement…"

Image non disponible
Accéder à VBE



Dans l'éditeur Visual Basic, une fenêtre blanche va vous proposer d'écrire un bout de code correspondant à l'événement attaché au chargement du formulaire.

 
Sélectionnez
Option Explicit
Option Compare Database

Private Sub Form_Load()

End Sub

Cet événement s'appelle Form_Load qui de par sa traduction signifie ce qu'il fait...

Pour plus d'informations sur les événements des contrôles et autres objets, veuillez vous référer à l'aide en ligne en mettant en surbrillance l'événement souhaité et appuyez sur la touche F1.

Rappelez-vous qu'un événement est quelque chose qui se passe lorsque l'utilisateur effectue une certaine opération "avant", "pendant", "après", etc. sur tel ou tel contrôle.

Il faut que vous sachiez qu'un contrôle de type zone de liste ne possède pas les mêmes événements d'un contrôle bouton de commande par exemple et pour autant, ils ont des événements communs.
Il vous appartiendra alors de choisir l'événement le plus approprié lorsque tel ou tel contrôle est utilisé dans votre formulaire si vous souhaitez en gérer d'autres ou bien changer ceux mis en place dans le code.

3-1-5-1. Description des événements du projet

Dans la classe du formulaire, un jeu d'un peu plus de mille (1000) lignes est à écrire pour que ce formulaire soit (presque) totalement opérationnel.
Il est vrai que cela peut paraître beaucoup, mais en fait, il y a beaucoup de blocs qui sont soit similaires soit dupliqués, car ils effectuent les mêmes actions. Et puis en parallèle, il y a aussi à considérer le nombre de lignes de commentaire.

Dans son ensemble, le code est assez simple en soi à comprendre, mais il faut tout de même avoir déjà manipulé ce langage, car en plus d'être dommage, il est plutôt frustrant de copier-coller du code sans comprendre à quoi il sert ou ce qu'il fait.

Liste des procédures et fonctions de la classe de formulaire

  • cboCurEnvironment_AfterUpdate : Procédure Private Déclenche l'événement AprèsMaj de la liste déroulante des environnements.
  • chkIsUSFormat_Click : Procédure Private Appelle l'événement Clic pour adapter le format de la date.
  • chkSharePointEnabled_Click : Procédure Private Appelle l'événement Clic permettant de définir si le site SharePoint est disponible.
  • cmdArchiveFolder_Click : Procédure Private Appelle l'événement Clic permettant de sélectionner le dossier d'archive.
  • cmdCheckSPAddress_Click : Procédure Private Appelle l'événement Clic permettant de sélectionner vérifier l'adresse SharePoint.
  • cmdDatabasePath_Click : Procédure Private Appelle l'événement Clic permettant de sélectionner le chemin de la base de données dorsale.
  • cmdExit_Click : Procédure Private Appelle l'événement Clic permettant de fermer le formulaire avec alerte.
  • cmdOutputFolder_Click : Procédure Private Appelle l'événement Clic permettant de sélectionner le dossier des fichiers générés.
  • cmdPasteSPAddress_Click : Procédure Private Appelle l'événement Clic permettant de coller l'adresse du site SharePoint.
  • cmdSave_Click : Procédure Private Appelle l'événement Clic permettant de sauvegarder l'environnement.
  • cmdWorkingPath_Click : Procédure Private Appelle l'événement Clic permettant de sélectionner le chemin de travail.
  • IsInUse_Click : Procédure Private Appelle l'événement Clic de la case à cocher de l'environnement.
  • Form_Load : Procédure Private Appelle l'événement SurChargement du formulaire.
  • Form_Timer : Procédure Private Appelle l'événement SurMinuterie du formulaire (clignotement de la zone de liste).
  • txtSharePointAddress_Change : Procédure Private Appelle l'événement SurChangement dans la zone de texte de l'adresse SharePoint.
  • txtSharePointAddress_KeyPress : Procédure Private Appelle l'événement SurToucheActivée dans la zone de texte de l'adresse SharePoint.
  • txtArchiveFolder_Change : Procédure Private Appelle l'événement SurChangement dans la zone de texte du dossier d'archives.
  • txtArchiveFolder_KeyPress : Procédure Private Appelle l'événement SurToucheActivée dans la zone de texte du dossier d'archives.
  • txtDataFileFolder_Change : Procédure Private Appelle l'événement SurChangement dans la zone de texte du dossier du fichier source.
  • txtDateFormat_Change : Procédure Private Appelle l'événement SurChangement dans la zone de texte du format de la date.
  • txtLinkedDatabaseName_Change : Procédure Private Appelle l'événement SurChangement dans la zone de texte de du nom de la base de données.
  • txtLinkedDatabasePath_Change : Procédure Private Appelle l'événement SurChangement dans la zone de texte du chemin de la base de données.
  • txtOutputFilename_Change : Procédure Private Appelle l'événement SurChangement dans la zone de texte du dossier du fichier de sortie.
  • txtOutputFolder_Change : Procédure Private Appelle l'événement SurChangement dans la zone du dossier où sont générés les fichiers.
  • txtOutputFolder_KeyPress : Procédure Private Appelle l'événement SurToucheActivée dans la zone de texte du dossier où sont générés les fichiers.
  • txtWorkingPath_Change : Procédure Private Appelle l'événement SurChangement dans la zone de texte du chemin de travail.
  • SelectedEnvironment : Propriété Get Private Propriété permettant de connaître le nouvel environnement sélectionné.
  • SelectedEnvironment : Propriété Let Private Propriété permettant de définir le nouvel environnement sélectionné.
  • CurrentEnvironment : Propriété Get Private Propriété permettant de connaître l'environnement en cours.
  • CurrentEnvironment : Propriété Let Private Propriété permettant de définir l'environnement en cours.
  • GetFolderForThisField : Function Private Procédure permettant de sélectionner un dossier.
  • GetRecordSource : Function Private Procédure permettant d'obtenir la source de données du formulaire.
  • InitializeControls : Procédure Private Procédure permettant d'initialiser les contrôles.
  • NewDataCompleted : Function Private Procédure permettant de vérifier si l'ensemble des informations saisies est correct.
  • PasteWebSiteAddress : Procédure Private Procédure permettant de coller l'adresse WEB pour le site SharePoint.
  • DisableAllEnvironments : Procédure Private Procédure permettant de désactiver tous les environnements.
  • EnableThisEnvironment : Procédure Private Procédure permettant d'activer un environnement particulier.

La liste ci-dessus n'est pas exhaustive ; telle qu'exposée ici, elle représente l'ensemble des procédures (événementielles ou non) qui sont appliquées à ce formulaire...

3-1-6. Rédaction du code du formulaire

J'ai tâché de découper les blocs de code par événement et procédures/fonctions privées de manière à ce que vous puissiez mieux aborder l'ensemble qui constitue l'intégralité du module de classe.
Le code est largement commenté pour que quiconque (avec un peu d'expérience tout de même) puisse comprendre toutes les lignes et les instructions en regard.
De nombreuses fonctions sont externalisées dans un autre module avec quelques déclarations publiques également ; nous verrons cela dans les paragraphes suivants...

Voici l'ensemble des blocs de code de la classe du formulaire :

3-1-6-1. En-tête du module

Dans l'en-tête du module, vous retrouvez des constantes et des variables privées.
Les trois constantes définissent respectivement le nom d'une table liée (ici, la table des utilisateurs), et les deux formats de date servant à être greffés au nom du fichier de sortie pour l'horodatage.
La variable m_blnDataChanged est exploitée dans tout le module pour servir de témoin aux changements apportés dans les contrôles Textboxes et les Cases à cocher. Vous voyez également les variables de membre des deux propriétés privées CurrentEnvironment et SelectedEnvironment servant à connaître et définir l'ID de l'environnement.
Les autres variables sont affectées lorsque des valeurs dans les contrôles sont affectés aussi. Elles concernent ici le format de la date inscrit dans le nom du fichier généré par le projet.

Rappelez-vous...

Rappelez-vous que lorsque je mentionne des termes comme «le projet», je parle bien entendu du clone du projet que j'ai évoqué au début de cet article. Bien entendu, ce que vous êtes en train de concevoir ne saura pas ni importer, ni exporter, ni générer des fichiers tel que cela est décrit dans plusieurs paragraphes.
Ce formulaire est construit pour ce type de projet et donc, les zones de texte requises y sont créées.

Code du module de classe du formulaire - déclarations
Sélectionnez
'**********************************************************************
' Module            : Form_frmEnvironmentManagement
' Type              : Document VBA
' DateTime          : 11/10/2012
' Author            : Jean-Philippe AMBROSINO
' Review            : Jean-Philippe AMBROSINO
' Review date       : 25/11/2012
' Purpose           : Classe du formulaire de définition de l'environnement
'
''**********************************************************************

Option Compare Database
Option Explicit

'Table liée par défaut
Private Const TBL_USERS                      As String = "tblUsers"
'Format des dates
Private Const FORMAT_DATE_FR                 As String = "ddmmyyyy"
Private Const FORMAT_DATE_US                 As String = "yyyymmdd"
'Flag des changements apportés aux contrôles
Private m_blnDataChanged                     As Boolean
'Statut de l'état
Private m_blnIsInUse                         As Boolean
'Environnement en cours
Private m_lngCurrentEnvironment              As Long
'Nouvel environnement sélectionné
Private m_lngSelectedEnvironment             As Long
'Format de la date du nom du fichier de sortie
Private m_strDateFormat                      As String


Private Property Get SelectedEnvironment() As Long
'Nouvel environnement sélectionné
    SelectedEnvironment = m_lngSelectedEnvironment
End Property

Private Property Let SelectedEnvironment(ByVal SelEnvironment As Long)
'Nouvel environnement sélectionné
    m_lngSelectedEnvironment = SelEnvironment
End Property

Private Property Get CurrentEnvironment() As Long
'Environnement en cours
    CurrentEnvironment = m_lngCurrentEnvironment
End Property

Private Property Let CurrentEnvironment(ByVal CurEnvironment As Long)
'Environnement en cours
    m_lngCurrentEnvironment = CurEnvironment
End Property
3-1-6-2. Classe du formulaire - Événements du formulaire

Le chargement Load du formulaire appelle la fonction GetRecordSource() pour remplir les contrôles via la procédure InitializeControls si elle est appelée avec succès. Sinon, une erreur est levée.

L'événement Timer fait clignoter un cadre et change la couleur et l'aspect gras de l'étiquette de la case à cocher.
Il surveille par la même l'état Boolean de la variable m_blnDataChanged pour affecter la propriété Enabled du bouton cmdSave qui, lorsqu'il est actif, signifie qu'un changement a été opéré et que donc, que la sauvegarde est possible.

Code du module de classe du formulaire - Événements SurChargement du formulaire (version de base)
Sélectionnez
Private Sub Form_Load()
'---------------------------------------------------------------------------
' Procedure     : Form_Load
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Chargement du formulaire
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim strErrDescription                        As String

    On Error GoTo L_ErrForm_Load
    'On initialise les données pour l'environnement enregistré (on suppose qu'il y en a un)
    If GetRecordSource(True, 0) = False Then
        'Sinon lève une erreur
        Err.Raise 3032, "Données source", "Impossible de récupérer les données de l'environnement"
    End If
    'On initialise les contrôles
    InitializeControls

    On Error GoTo 0
L_ExForm_Load:
    Exit Sub

L_ErrForm_Load:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExForm_Load
End Sub

Comme dit plus haut, voici le bloc de code qui traite le chargement du formulaire qui alimente dynamiquement la liste déroulante des environnements en considérant la valeur de la constante de l'environnement réservé au développeur.
Ce bloc est totalement facultatif et si vous n'êtes pas concerné par ce cas, vous prendrez le bloc ci-dessus.

Code du module de classe du formulaire - Événements SurChargement du formulaire (version Réservé au Développeur)
Sélectionnez
Private Sub Form_Load()
'---------------------------------------------------------------------------
' Procedure     : Form_Load
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Chargement du formulaire
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim strErrDescription                        As String
Dim SQLEnvList                               As String


    On Error GoTo L_ErrForm_Load
    'Chaîne SQL listant les environnement selon la constante RESERVED_TO_DEVELOPER
    SQLEnvList = "SELECT EnvironmentID, EnvironmentName FROM tblProcessEnvironment "
    SQLEnvList = SQLEnvList & "WHERE ReservedToDeveloper=" & CInt(RESERVED_TO_DEVELOPER) & ";"
    'Affectation de la clause SQL à la liste déroulante
    With cboCurEnvironment
        .ColumnWidths = "0cm;2cm"
        .BoundColumn = 1
        .LimitToList = True
        .ColumnCount = 2
        .RowSource = SQLEnvList
    End With
    'On initialise les données pour l'environnement enregistré (on suppose qu'il y en a un)
    If GetRecordSource(True, 0) = False Then
        'Sinon lève une erreur
        Err.Raise 3032, "Données source", "Impossible de récupérer les données de l'environnement"
    End If
    'On initialise les contrôles
    InitializeControls

    On Error GoTo 0
L_ExForm_Load:
    Exit Sub

L_ErrForm_Load:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExForm_Load
End Sub

Le formulaire délenche aussi l'événement SurMinuterie().

Code du module de classe du formulaire - Événements SurMinuterie du formulaire
Sélectionnez
Private Sub Form_Timer()
'---------------------------------------------------------------------------
' Procedure     : Form_Timer
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Timer sur le formulaire (fait clignoter l'environnement actif)
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

    On Error Resume Next
    'Si l'environnement est actif
    If Nz(Me.IsInUse, False) Then
        'On change l'aspect de l'étiquette et de son cadre
        With Me.lblIsInUse
            DoEvents
            .ForeColor = IIf(.ForeColor = 255, 10053222, 255)
            .FontBold = (.ForeColor = 255)
            Me.shpIsInUse.Visible = (.ForeColor = 255)
        End With
    End If
    'On change la couleur le l'étiquette de l'adresse SharePoint
    If Nz(Me.chkSharePointEnabled, False) Then
        Me.lblSharePointEnabled.FontBold = (Me.lblIsInUse.ForeColor = 255)
    End If
    'On active le bouton de sauvegarde si un changement a été opéré (flag)
    cmdSave.Enabled = m_blnDataChanged
End Sub
3-1-6-3. Classe du formulaire - Evénement AprèsMaj de la zone de liste déroulante

Lors de la sélection d'un nouvel environnement, l'événement AfterUpdate de la liste des environnements charge le nouvel environnement en appelant la fonction GetRecordSource() et permet de réaffecter les contrôles en conséquence.

Evénement AprèsMaj de la zone de liste déroulante
Sélectionnez
Private Sub cboCurEnvironment_AfterUpdate()
'---------------------------------------------------------------------------
' Procedure     : cboCurEnvironment_AfterUpdate
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet de choisir un autre environnemnet
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim strErrDescription                        As String
    On Error GoTo L_ErrcboCurEnvironment_AfterUpdate
    'La propriété prend la valeur de la liste
    SelectedEnvironment = Me.cboCurEnvironment
    'Si l'on peut obtenir l'environnement dans la table...
    If GetRecordSource(False, SelectedEnvironment) = False Then
        'Sinon, on lève l'erreur
        Err.Raise 3032, "Données source", "Impossible de récupérer les données de l'environnement"
    End If
    'On alimente les étiquettes
    'du titre de la fenêtre
    Me.lblData.Caption = "Les paramètres pour l'environnement " & Me.cboCurEnvironment.Column(1) & " :"
    'Met en gras l'étiquette de l'adresse SharePoint si la case est cochée
    Me.lblSharePointEnabled.FontBold = Me.chkSharePointEnabled
    'On désactive le bouton de sauvegarde
    Me.cmdSave.Enabled = False

    On Error GoTo 0
L_ExcboCurEnvironment_AfterUpdate:
    Exit Sub

L_ErrcboCurEnvironment_AfterUpdate:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExcboCurEnvironment_AfterUpdate
End Sub
3-1-6-4. Classe du formulaire - Événements Click des cases à cocher

Lorsque les cases à cocher son affectées avec leurs valeurs True ou False, l'évenement Click est déclenché.

  - IsInUse_Click : Vérifie la modification de l'affectation du nouvel environnement avec affectation de l'intervalle du Timer en conséquence ;

  - chkIsUSFormat_Click : Attribut le format US pour les dates du nom de fichier de sortie si elle est égale à True ,;

  - chkSharePointEnabled_Click : Change la valeur de la variable flag m_blnDataChanged selon sa valeur et permet d'assurer la validité de l'adresse du site SharePoint s'il en existe un.

Rappel

La définition du site SharePoint reste totalement facultative et hors contexte si votre entreprise ne possède pas de base documentaire du même nom.

Événements Click des cases à cocher
Sélectionnez
Private Sub IsInUse_Click()
'---------------------------------------------------------------------------
' Procedure     : IsInUse_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Vérifie la modification de l'affectation du nouvel environnement
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim strErrDescription                        As String
    On Error GoTo L_ErrIsInUse_Click
    'On compare les environnements
    If SelectedEnvironment <> CurrentEnvironment Then
        'On averti en conséquence
        If  MsgBox("Seulement un seul environnement peut être affecté au statut 'Actif'" _
            & vbCrLf & "Si vous définissez l'environnement '" & Me.cboCurEnvironment.Column(1) & _
            "' comme nouvel environnement, tous les autres seront désactivés..." & vbCrLf & vbCrLf & _
            "Souhaitez-vous définir cet environnement comme environnement de travail actuel ?", _
            vbExclamation + vbYesNo + vbDefaultButton2, "Definir l'environnement") _
            = vbYes Then
            'IOn desactive alors tous les environnements (conflits éventuels avec intervention directe dans la table)
            DisableAllEnvironments
            'Et on active celui sélectionné
            EnableThisEnvironment Me.cboCurEnvironment
            'On obtient les info de la table pour cet environnement
            If GetRecordSource(False, Me.cboCurEnvironment.Value) = False Then
                'Si il n'y a pas de données
                Err.Raise 3032, "Données source", "Impossible de récupérer les données de l'environnement"
            End If
            'Affecte le flag de changement dans le formulaire
            m_blnDataChanged = True
            'On modifie l'interval du Timer
            Me.TimerInterval = 600
        Else
            'On décoche la case de l'environnement actif
            Me.IsInUse.Value = False
            'On désactive le Timer
            Me.TimerInterval = 0
        End If
        'On libère l'état du bouton de sauvegarde
        Me.cmdSave.Enabled = Nz(Me.IsInUse, False)
    Else
        'On averti les mauvaise manip pour la gestion des environnements
        MsgBox "Il n'est pas possible de désactiver un enviromnent qui est définit comme environnement de travail actuel." _
            & vbCrLf & vbCrLf & "Si vous souhaitez désactiver l'environnement '" & Me.cboCurEnvironment.Column(1) & _
            "', vous devez d'abord choisir l'environnement que vous souhaitez activer, et donc, celui-ci est désactivé automatiquement.", _
            vbCritical, "Opération refusée"
        'On force la case à cocher
        Me.IsInUse.Value = True
    End If

    On Error GoTo 0
L_ExIsInUse_Click:
    Exit Sub

L_ErrIsInUse_Click:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExIsInUse_Click
End Sub

Private Sub chkIsUSFormat_Click()
'---------------------------------------------------------------------------
' Procedure     : chkIsUSFormat_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Case à cocher du format US pour les dates
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Initialise la variable de module du format pour un usage ultérieur
    m_strDateFormat = Nz(Me.txtDateFormat, vbNullString)
    'Vérifie la longueur de la chaîne
    If Len(m_strDateFormat) Then
        'Si la case format US est cochée
        If Nz(Me.chkIsUSFormat, False) Then
            Me.txtDateFormat = FORMAT_DATE_US
        Else
            Me.txtDateFormat = m_strDateFormat
        End If
    Else
        'Sinon le format est FR
        Me.txtDateFormat = FORMAT_DATE_FR
    End If
End Sub

Private Sub chkSharePointEnabled_Click()
'---------------------------------------------------------------------------
' Procedure     : chkSharePointEnabled_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Change le statut du flag m_blnDataChanged puisque changement effectué dans la zone texte
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = True
    'Met en gras l'étiquette de l'adresse SharePoint si la case est cochée
    Me.lblSharePointEnabled.FontBold = Me.chkSharePointEnabled.Value
    'Active le bouton de vérification de l'adresse si la case à cocher est cochée
    Me.cmdCheckSPAddress.Enabled = Me.chkSharePointEnabled.Value
End Sub
3-1-6-5. Classe du formulaire - Sélection des dossiers

La sélection des dossiers est opérée par la même fonction GetFolderForThisField() qui exploite la propriété ControlTipText du contrôle appelant pour définir le titre de la boîte de dialogue, la zone de texte cible recevant le nom du dossier et une variable témoin "blnHasChanged" qui affecte la variable de module m_blnDataChanged pour savoir si effectivement un (autre) dossier a été sélectionné.

Si un dossier a effectivement été sélectionné, alors la variable témoin donne sa valeur True à la variable de module m_blnDataChanged ce qui fait que le l'événement Timer du formulaire va réagir en conséquence vis à vis du bouton de commande cmdSave.

Sélection des dossiers
Sélectionnez
Private Sub cmdArchiveFolder_Click()
'---------------------------------------------------------------------------
' Procedure     : cmdArchiveFolder_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet de sélectionner un dossier pour les archives
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim blnHasChanged                            As Boolean
    'Appelle GetFolderForThisField pour obtenir le dossier d'archive
    Me.txtArchiveFolder = GetFolderForThisField(cmdArchiveFolder.ControlTipText, Nz(Me.txtArchiveFolder.Value, vbNullString), blnHasChanged)
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = blnHasChanged
End Sub

Private Sub cmdDataFileFolder_Click()
'---------------------------------------------------------------------------
' Procedure     : cmdDataFileFolder_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet de sélectionner un dossier où sont les fichiers sources de données
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim blnHasChanged                            As Boolean
    'Appelle GetFolderForThisField pour obtenir le dossier des fichiers sources
    Me.txtDataFileFolder = GetFolderForThisField(cmdDataFileFolder.ControlTipText, Nz(Me.txtDataFileFolder.Value, vbNullString), blnHasChanged)
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = blnHasChanged

End Sub

Private Sub cmdOutputFolder_Click()
'---------------------------------------------------------------------------
' Procedure     : cmdOutputFolder_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet de sélectionner un dossier pour le fichier de sortie
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim blnHasChanged                            As Boolean
    'Appelle GetFolderForThisField pour obtenir le dossier des fichier de sortie
    Me.txtOutputFolder = GetFolderForThisField(cmdOutputFolder.ControlTipText, Nz(Me.txtOutputFolder.Value, vbNullString), blnHasChanged)
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = blnHasChanged
End Sub
3-1-6-6. Classe du formulaire - Sélection des chemins

La sélection du chemin de la base de données dorsale est opérée par l'appel de la fonction SelectAnyFile() qui alimente la variable strDBFullPath passée en paramètre.
Cette dernière est ensuite décomposée par les fonctions GetPathName et GetFileName pour respectivement alimenter la zone de texte du chemin et la zone de texte du nom de ladite base de données.

Le sélection du chemin de travail est opérée par l'appel de la fonction SelectAnyFolder(). De ce chemin de travail découlera la définition du dossier Racine duquel seront créés (par votre application s'ils n'existent pas) les dossiers définis dans les autres zones de texte.
  - En effet, si vous sélectionnez les dossiers, c'est que ces derniers existent...
  - Si vous les saisissez à la main, c'est que vous souhaitez qu'ils soient créés dynamiquement par votre procédure de traitement des fichiers.

Sélection des chemins
Sélectionnez
Private Sub cmdDatabasePath_Click()
'---------------------------------------------------------------------------
' Procedure     : cmdDatabasePath_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet de sélectionner un dossier pour la base de données dorsale
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim blnHasChanged                            As Boolean
Dim strDBFullPath                            As String
Dim strDBPathName                            As String
Dim strDBFileName                            As String
    'Permet de sélectionner un fichier
    strDBFullPath = SelectAnyFile(Me.HWnd, cmdDatabasePath.ControlTipText, Nz(Me.txtLinkedDatabasePath.Value, vbNullString))
    'Affecte le flag de changement dans le formulaire en vérifiant que le fichier existe
    m_blnDataChanged = ThisFileExists(strDBFullPath)
    'S'il existe alors...
    If m_blnDataChanged Then
        'Les zones de texte reçoivent pour l'une le chemin et pour l'autre le nom de la base de données dorsale
        Me.txtLinkedDatabasePath = GetPathName(strDBFullPath, False)
        Me.txtLinkedDatabaseName = GetFileName(strDBFullPath)
    End If
End Sub

Private Sub cmdWorkingPath_Click()
'---------------------------------------------------------------------------
' Procedure     : cmdWorkingPath_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet de sélectionner un dossier pour définir le chemin de travail
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim strOldWorkingPath                        As String
Dim strNewWorkingPath                        As String
Dim strStartDirectory                        As String

    On Error GoTo L_ErrcmdWorkingPath_Click
    'Initialise l'ancien chemin de travail
    strOldWorkingPath = Nz(Me.WorkingPath.Value, vbNullString)
    'Défini le dossier de démarrage pour la sélection
    strStartDirectory = CurrentProject.Path
    'Initialise le nouveau chemin de travail
    strNewWorkingPath = SelectAnyFolder("Sélectionner le dossier", strStartDirectory)
    'Si un chemin a été selectionné...
    If Len(strNewWorkingPath) Then chemin sont identiques
        If StrComp(strOldWorkingPath, strNewWorkingPath, 1) = 0 Then
            'On lève une erreur
            Err.Raise 58, "Dossier identique", "Le chemin d'accès sélectionné est le même que celui en cours !"
        End If
        'Sinon, on rempli la zone de texte avec le nouveau chemin
        'Si l'ancien et le nouveau
        Me.txtWorkingPath = strNewWorkingPath
        'Affecte le flag de changement dans le formulaire
        m_blnDataChanged = True
    End If

    On Error GoTo 0
L_ExcmdWorkingPath_Click:
    Exit Sub

L_ErrcmdWorkingPath_Click:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExcmdWorkingPath_Click
End Sub
3-1-6-7. Classe du formulaire - Sauvegarde de l'environnement

La sauvegarde de l'environnement passe par l'événement Click du bouton de commande cmdSave
Dans son exécution, l'appel de la fonction NewDataCompleted() permet de vérifier la cohérence des informations saisies et retourne False en donnant le Focus au contrôle concerné accompagné d'un message circonstanciel si toutefois une zone a été omise ou n'est pas conforme.
Voir exemple ici
Si tout est correct, alors une procédure d'écriture dans la table locale est exécutée par le biais d'un Recordset.

Sauvegarde de l'environnement
Sélectionnez
Private Sub cmdSave_Click()
'---------------------------------------------------------------------------
' Procedure     : cmdSave_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Sauvegarde l'environnement
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim strErrDescription                        As String
Dim oDB                                      As DAO.Database
Dim oRS                                      As DAO.Recordset
Dim strMissedValue                           As String
Dim SQLSelect                                As String
Dim SQLUpdate                                As String
Dim strCriteria                              As String
Dim blnIsInUse                               As Boolean
Dim lngEnvironmentId                         As Long
Dim strEnvironment                           As String
Dim strSharePointAddress                     As String
Dim strLinkedDatabasePath                    As String
Dim strLinkedDatabaseName                    As String
Dim strWorkingPath                           As String
Dim strDataFileFolder                        As String
Dim strArchiveFolder                         As String
Dim strOutputFilename                        As String
Dim strDateFormat                            As String
Dim strFileExtension                         As String
Dim strOutputFolder                          As String
Dim blnSharePointEnabled                     As Boolean
Dim blnIsUSFormat                            As Boolean

Dim blnSPPEnabled                            As Boolean
Dim strSPPAddress                            As String
Dim t                                        As Integer

    On Error GoTo L_ErrcmdSave_Click
    'Sablier
    DoCmd.Hourglass True
    'On affiche le traitement
    Me.lblSaveInformation.Caption = "Vérification des données avant la mise à jour de cet environnement..."
    DoEvents
    Sleep 1000
    'On affecte l'environnement en cours
    strEnvironment = Me.cboCurEnvironment.Column(1)
    'On vérifie que tous les champs sont correctement remplis
    If NewDataCompleted(strMissedValue) = False Then
        'Sinon, on lève une erreur
        Err.Raise 3032, "Enregistrement non sauvé", "Impossible de mettre à jour l'environnement actuel '" & strEnvironment _
            & "'  parce que les données du champ '" & UCase$(strMissedValue) & "' est manquante ou invalide !"
    End If
    'On affecte les variables
    'L'environnement (ID)
    lngEnvironmentId = Nz(Me.EnvironmentID.Value, 0)
    'Le flag de l'activation
    blnIsInUse = Nz(Me.IsInUse.Value, False)
    'Le chemin de travail
    strWorkingPath = Nz(Me.txtWorkingPath.Value, vbNullString)
    'Le dossier de sortie
    strOutputFolder = Nz(Me.txtOutputFolder.Value, vbNullString)
    'Le dossier d'archive
    strArchiveFolder = Nz(Me.txtArchiveFolder.Value, vbNullString)
    'Le flag de l'activation du site SharePoint
    blnSharePointEnabled = Nz(Me.chkSharePointEnabled.Value, False)
    'L'adresse du site SharePoint
    strSharePointAddress = Nz(Me.txtSharePointAddress.Value, vbNullString)
    'Le chemin de la base de données dorsale
    strLinkedDatabasePath = Nz(Me.txtLinkedDatabasePath.Value, vbNullString)
    'Le nom de la base de données dorsale
    strLinkedDatabaseName = Nz(Me.txtLinkedDatabaseName.Value, vbNullString)
    'On change la liaison de la base de données dorsale
    If ChangeLinkedDatabase(strLinkedDatabasePath, strLinkedDatabaseName) = False Then
        'Si la fonction échoue, une erreur est levée
        Err.Raise 3184, "Echec de liaison", "Une erreur a été rencontrée pendant l'opération de réattachement des tables."
    End If
    'On vérifie l'existence du chemin de travail
    If ThisFolderExists(strWorkingPath) = False Then
        Err.Raise 76, "Chemin d'accès inexistant", "Le chemin d'accès :" & vbCrLf & Chr(171) & strWorkingPath & Chr(187) & vbCrLf & _
            "n'existe pas ou bien vos droits d'accès pour accéder à ce répertoire sont restrictifs." & vbCrLf & vbCrLf & _
            "Contactez votre support informatique pour obtenir la raison pour laquelle vous n'avez pas été en mesure de choisir un " _
            & "répertoire qui n'est pas disponible actuellement..."
    End If
    'On informe l'utilisateur de ce qui se passe
    Me.lblSaveInformation.Caption = "Vérification de la disponiblité du site SharePoint..."
    DoEvents
    'On affecte une variable temporaire
    blnSPPEnabled = blnSharePointEnabled
    'Puis l'adresse du site
    strSPPAddress = strSharePointAddress
    'Si le site est activé
    If blnSPPEnabled Then
        'Si le site est disponible
        If IsSharePointURLIsAvailable(strSPPAddress) = False Then
            'Sinon, on lève une erreur
            Err.Raise 404, "Site SPP injoignable", "L'adresse du site SharePoint :" & vbCrLf & Chr(171) & _
                strSPPAddress & Chr(187) & vbCrLf & "est signalé comme disponible, mais il n'est pas possible d'accéder au site..." _
                & vbCrLf & vbCrLf & "Modifier l'état ou vérifiez l'adresse pour voir pourquoi elle n'est pas disponible."
        End If
    End If
    'On défini la BDD en cours dans la variable
    Set oDB = CurrentDb
    'On construit la chaîne SQl du RecordSet
    SQLSelect = "SELECT EnvironmentID, IsInUse, ReservedToDeveloper, EnvironmentName, LinkedDatabasePath, LinkedDatabaseName, WorkingPath, DataFileFolder, " _
        & "OutputFolder, OutputFilename, FileExtension, DateFormat, IsUSFormat, ArchiveFolder, SharePointEnabled, SharePointAddress "
    SQLSelect = SQLSelect & "FROM tblProcessEnvironment "
    SQLSelect = SQLSelect & "WHERE (EnvironmentID=" & SelectedEnvironment & ") AND (ReservedToDeveloper=False);"
    'On ouvre le RecordSet pour modification
    Set oRS = oDB.OpenRecordset(SQLSelect, 2)
    With oRS
        .Edit
        .Fields("EnvironmentID").Value = lngEnvironmentId
        .Fields("IsInUse").Value = CInt(blnIsInUse)
        .Fields("EnvironmentName").Value = strEnvironment
        .Fields("LinkedDatabasePath").Value = strLinkedDatabasePath
        .Fields("LinkedDatabaseName").Value = strLinkedDatabaseName
        .Fields("WorkingPath").Value = strWorkingPath
        .Fields("DataFileFolder").Value = strDataFileFolder
        .Fields("OutputFolder").Value = strOutputFolder
        .Fields("OutputFilename").Value = strOutputFilename
        .Fields("FileExtension").Value = strFileExtension
        .Fields("DateFormat").Value = strDateFormat
        .Fields("IsUSFormat").Value = blnIsUSFormat
        .Fields("ArchiveFolder").Value = strArchiveFolder
        .Fields("SharePointEnabled").Value = blnSharePointEnabled
        .Fields("SharePointAddress").Value = strSharePointAddress
        .Update
        .Close
    End With
    'On affiche le message de succès
    MsgBox "L'environnement '" & strEnvironment & "' est maintenant défini comme environnement de travail et a été mis à jour avec ses caractéristiques.", _
        vbInformation, "Changements pris en compte"
    'On ferme alors le formulaire
    DoCmd.Close acForm, Me.Name
    'On ferme l'objet Database
    If Not oDB Is Nothing Then oDB.Close
    On Error GoTo 0
L_ExcmdSave_Click:
    'On libère les objets
    Set oDB = Nothing
    Set oRS = Nothing
    'On restitue le cuirseur
    DoCmd.Hourglass False
    Exit Sub

L_ErrcmdSave_Click:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExcmdSave_Click
End Sub
3-1-6-8. Classe du formulaire - Annulation ou sortie du formulaire

Si la variable m_blnDataChanged est à True, alors un message vous demande de confirmer l'annulation.
Sinon, eh bien le bouton porte la légende Fermer et la condition ne s'y prête donc pas.

Annulation ou sortie du formulaire
Sélectionnez
Private Sub cmdExit_Click()
'---------------------------------------------------------------------------
' Procedure     : cmdExit_Click
' DateTime      : 11/11/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Evénement déclenché quand l'utilisateur veut fermer ou annuler
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    Select Case cmdExit.Caption
        'Si le bouton de sortie porte la légende Annuler
        Case "&Annuler"
            If m_blnDataChanged Then
                If MsgBox("Vous souhaitez annuler la nouvelle entrée et fermer ?", _
                    vbQuestion + vbYesNo + vbDefaultButton2, "Annuler") = vbNo Then
                    Exit Sub
                End If
            End If
        'Sinon
        Case "&Fermer"
    End Select
    'On ferme le formulaire
    DoCmd.Close acForm, Me.Name
End Sub
3-1-6-9. Classe du formulaire - Vérification et collage de l'adresse SharePoint

La vérification de l'adresse est opérée par le biais de la fonction ShellOpenExec() qui tente d'ouvrir la page et retourne une valeur supérieure à 32 si c'est positif.

Le collage exploite un objet Clipboard initialisé par un DataObject via la fonction PasteWebSiteAddress qui vérifie par la même que le presse-papier n'est pas vide...

Vérification et collage de l'adresse SharePoint
Sélectionnez
Private Sub cmdCheckSPAddress_Click()
'---------------------------------------------------------------------------
' Procedure     : cmdCheckSPAddress_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Vérifie la validité de l'adresse web
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         : ShellOpenExec retourne une valeur supérieure à 32 en cas de succès
'---------------------------------------------------------------------------

Dim strErrorReason                           As String
    'Vérifie la validité de l'adresse du site ne tentant de l'ouvrir
    If ShellOpenExec(Me.SharePointAddress.Value, Application.hWndAccessApp, , , strErrorReason) < 33 Then
        'Si l'erreur n'est pas interprétée mais qu'il y a une erreur (donc < à 33)
        If Len(strErrorReason) = 0 Then
            strErrorReason = "The page '" & Me.SharePointAddress.Value & "'" & vbCrLf & "is not pointing to a valid address."
        End If
        'On affiche le message
        MsgBox strErrorReason, vbExclamation, "Impossible d'ouvrir la page"
    End If
End Sub

Private Sub cmdPasteSPAddress_Click()
'---------------------------------------------------------------------------
' Procedure     : cmdPasteSPAddress_Click
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet de coller une adresse pour le site SharePoint
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    
    PasteWebSiteAddress "SharePointAddress", "l'adresse du site SharePoint"
End Sub
3-1-6-10. Classe du formulaire - Événements KeyPress sur les contrôles Textboxes

Les événements KeyPress sont exploités dans les contrôles Textbox pour empêcher la frappe de caractères interdits pour définir un nom de dossier ou une URL selon le cas.

Événements KeyPress sur les contrôles Textboxes
Sélectionnez
Private Sub txtArchiveFolder_KeyPress(KeyAscii As Integer)
'---------------------------------------------------------------------------
' Procedure     : txtArchiveFolder_KeyPress
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Interdit ou autorise la frappe de certains caractères
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    
    KeyAscii = basApplication.CheckFolderName(KeyAscii)
End Sub

Private Sub txtOutputFolder_KeyPress(KeyAscii As Integer)
'---------------------------------------------------------------------------
' Procedure     : txtOutputFolder_KeyPress
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Interdit ou autorise la frappe de certains caractères
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

    KeyAscii = basApplication.CheckFolderName(KeyAscii)
End Sub

Private Sub txtSharePointAddress_KeyPress(KeyAscii As Integer)
'---------------------------------------------------------------------------
' Procedure     : txtSharePointAddress_KeyPress
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Interdit ou autorise la frappe de certains caractères
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

    KeyAscii = basApplication.CheckFolderName(KeyAscii, True)
End Sub
3-1-6-11. Classe du formulaire - Événements Change sur les contrôles Textboxes

Les événements Change sur les contrôles Textboxes permettent d'affecter l'état de la variable m_blnDataChanged pour déterminer le fait qu'un changement a été opéré.

Ce choix et cette façon de faire est plus fiable que la propriété Dirty pour ma part.

Événements Change sur les contrôles Textboxes
Sélectionnez
Private Sub txtArchiveFolder_Change()
'---------------------------------------------------------------------------
' Procedure     : txtArchiveFolder_Change
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Change le statut du flag m_blnDataChanged puisque changement effectué dans la zone texte
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = True
End Sub

Private Sub txtDataFileFolder_Change()
'---------------------------------------------------------------------------
' Procedure     : txtDataFileFolder_Change
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Change le statut du flag m_blnDataChanged puisque changement effectué dans la zone texte
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = True
End Sub

Private Sub txtDateFormat_Change()
'---------------------------------------------------------------------------
' Procedure     : txtDateFormat_Change
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Change le statut du flag m_blnDataChanged puisque changement effectué dans la zone texte
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = True
End Sub

Private Sub txtLinkedDatabaseName_Change()
'---------------------------------------------------------------------------
' Procedure     : txtLinkedDatabaseName_Change
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Change le statut du flag m_blnDataChanged puisque changement effectué dans la zone texte
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = True
End Sub

Private Sub txtLinkedDatabasePath_Change()
'---------------------------------------------------------------------------
' Procedure     : txtLinkedDatabasePath_Change
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Change le statut du flag m_blnDataChanged puisque changement effectué dans la zone texte
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = True
End Sub

Private Sub txtOutputFilename_Change()
'---------------------------------------------------------------------------
' Procedure     : txtOutputFilename_Change
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Change le statut du flag m_blnDataChanged puisque changement effectué dans la zone texte
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = True
End Sub

Private Sub txtOutputFolder_Change()
'---------------------------------------------------------------------------
' Procedure     : txtOutputFolder_Change
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Change le statut du flag m_blnDataChanged puisque changement effectué dans la zone texte
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = True
End Sub

Private Sub txtSharePointAddress_Change()
'---------------------------------------------------------------------------
' Procedure     : txtSharePointAddress_Change
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Change le statut du flag m_blnDataChanged puisque changement effectué dans la zone texte
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = True
End Sub

Private Sub txtWorkingPath_Change()
'---------------------------------------------------------------------------
' Procedure     : txtWorkingPath_Change
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Change le statut du flag m_blnDataChanged puisque changement effectué dans la zone texte
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Affecte le flag de changement dans le formulaire
    m_blnDataChanged = True
End Sub
3-1-6-12. Classe du formulaire - Procédures et Fonction privées de la classe

La classe du formulaire possède ses fonctions et procédures privées qui lui sont propres dans le sens où elles n'ont pas lieues d'être partagées ; elles ne sollicitent que les contrôles de ce formulaires et affectent que les données de ce formulaire.
Vous pouvez dénombrer sept de ces fonctions et procédures :

  • InitializeControls
  • DisableAllEnvironments
  • EnableThisEnvironment
  • GetFolderForThisField
  • GetRecordSource
  • NewDataCompleted
  • PasteWebSiteAddress

Vous retrouvez le rôle de ces dernières en cliquant ici.

Procédures et Fonction privées de la classe
Sélectionnez
Private Sub InitializeControls()
'---------------------------------------------------------------------------
' Procedure     : InitializeControls
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Intialise les contrôles
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------
    'Donne le focus au bouton de fermeture
    Me.cmdExit.SetFocus
    'Met en gras l'étiquette de l'adresse SharePoint si la case est cochée
    Me.lblSharePointEnabled.FontBold = Me.chkSharePointEnabled
    'L'étiquette des information de sauvegarde est vidée
    Me.lblSaveInformation.Caption = vbNullString
    'Active le bouton de vérification de l'adresse si la case à cocher est cochée
    Me.cmdCheckSPAddress.Enabled = Nz(Me.chkSharePointEnabled.Value, False)
End Sub

Private Sub DisableAllEnvironments()
'---------------------------------------------------------------------------
' Procedure     : DisableAllEnvironments
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Désactive tous les environnements
'...........................................................................
' Parameters    : None

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim SQLUpdate                                As String

    On Error GoTo L_ErrDisableAllEnvironments
    SQLUpdate = "UPDATE tblProcessEnvironment SET IsInUse = False;"
    'Exécution du script SQL avec gestion d'erreur
    CurrentDb.Execute SQLUpdate, dbFailOnError
    On Error GoTo 0
L_ExDisableAllEnvironments:
    Exit Sub

L_ErrDisableAllEnvironments:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExDisableAllEnvironments

End Sub

Private Sub EnableThisEnvironment(ByVal CurEnvironment As Long)
'---------------------------------------------------------------------------
' Procedure     : EnableThisEnvironment
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Active cet environnment comme environnement par défaut
'...........................................................................
' Parameters    : CurEnvironment = ID de l'environnment en cours

' Return Codes  :  = True if so success
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim SQLUpdate                                As String

    On Error GoTo L_ErrDisableAllEnvironments
    SQLUpdate = "UPDATE tblProcessEnvironment SET IsInUse = True "
    SQLUpdate = SQLUpdate & "WHERE (EnvironmentID=" & CurEnvironment & ") AND (ReservedToDeveloper=False);"
    'Exécution du script SQL avec gestion d'erreur
    CurrentDb.Execute SQLUpdate, dbFailOnError
    On Error GoTo 0
L_ExDisableAllEnvironments:
    Exit Sub

L_ErrDisableAllEnvironments:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExDisableAllEnvironments
End Sub


Private Function GetFolderForThisField(ByVal Title As String, ByVal CurrentFolder As String, ByRef HasChanged As Boolean) As String
'---------------------------------------------------------------------------
' Procedure     : GetFolderForThisField
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Retourne le dossier sélectionné après vérification de la conformité
'...........................................................................
' Parameters    : Title = Titre du dialogue
'                 CurrentFolder = Dossier source
'                 HasChanged = True si nouveau dossier
'                 SelectFolderOnly = True si l'on ouvre le dialogue des dossiers
'                 ReturnFullPath = True si l'on retourne le chemin complet
'
' Return Codes  : String = True if so success
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim strNewFieldFolder                        As String
Dim strNewFieldPath                          As String
Dim strStartDirectory                        As String

    On Error GoTo L_ErrGetFolderForThisField
    'Le paramètre est false car aucun dossier n'est encore sélectionné
    HasChanged = False
    'On effecte le dossier de départ
    strStartDirectory = GetPathName(GetLinkedDatabasePath(CurrentDb, TBL_USERS, False), False)
    'L'utilisateur sélectionne un dossier
    strNewFieldPath = SelectAnyFolder("Sélectionner le dossier", strStartDirectory)
    'Si un dossier a été effectivement sélectionné
    If Len(strNewFieldPath) Then
        'On compare les dossier
        If StrComp(CurrentFolder, strNewFieldPath, 1) = 0 Then
            'Et on lève une erreur s'ils sont identiques
            Err.Raise 58, "Dossier identique", "Le dossier que vous avez sélectionné est le même que celui en cours !"
        End If
        'On éclate le chemin pour obtenir le dossier (qui sera un enfant du chemin de travail)
        strNewFieldFolder = Mid$(strNewFieldPath, InStrRev(strNewFieldPath, "\") + 1)
        'Le flage est a True car il y a eut changement
        HasChanged = True
    Else
        'Sinon, le doissier est conservé tel que
        strNewFieldFolder = CurrentFolder
    End If

    On Error GoTo 0
L_ExGetFolderForThisField:
    'La fonction retourne le nom du dossier
    GetFolderForThisField = strNewFieldFolder
    Exit Function

L_ErrGetFolderForThisField:
    strNewFieldFolder = CurrentFolder
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExGetFolderForThisField
End Function

Private Function GetRecordSource(ByVal FormLoaded As Boolean, ByVal EnvironmentSelected As Long) As Boolean
'---------------------------------------------------------------------------
' Procedure     : GetRecordSource
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet d'obtenir l'environnment en cours définit dans la table
'...........................................................................
' Parameters    : FormLoaded = True sur le formulaire est chargé auquel cas, la condition where s'adapte
'                 EnvironmentSelected = ID de l'environnement en cours
'
' Return Codes  : Boolean = True if so success
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim SQLSelect                                As String
Dim oDB                                      As DAO.Database
Dim oRS                                      As DAO.Recordset
Dim strErrorMissingParameter                 As String

    On Error GoTo L_ErrGetRecordSource
    'On construit la chaîne SQL pour remplir le formulaire
    SQLSelect = "SELECT EnvironmentID, IsInUse, ReservedToDeveloper, EnvironmentName, LinkedDatabasePath, LinkedDatabaseName, WorkingPath, DataFileFolder, " _
        & "OutputFolder, OutputFilename, FileExtension, DateFormat, IsUSFormat, ArchiveFolder, SharePointEnabled, SharePointAddress "
    SQLSelect = SQLSelect & "FROM tblProcessEnvironment "
    'Si le paramètre FormLoaded est True
    If FormLoaded Then
        'On prend l'environnment actif
        SQLSelect = SQLSelect & "WHERE (IsInUse=True) AND (ReservedToDeveloper=False);"
    Else
        'Sinon, on prend l'environnement choisi
        SQLSelect = SQLSelect & "WHERE (EnvironmentID=" & EnvironmentSelected & ") AND (ReservedToDeveloper=False);"
    End If
    'On initialise l'objet Database
    Set oDB = CurrentDb
    'On ouvre le RecordSet
    Set oRS = oDB.OpenRecordset(SQLSelect, 2)
    'Et on rempli les contrôles
    With oRS
        Me.EnvironmentID.Value = .Fields("EnvironmentID").Value
        Me.IsInUse.Value = Nz(.Fields("IsInUse").Value, False)
        Me.EnvironmentName.Value = .Fields("EnvironmentName").Value
        Me.cboCurEnvironment = .Fields("EnvironmentID").Value
        Me.txtLinkedDatabaseName = Nz(.Fields("LinkedDatabasePath").Value, vbNullString)
        Me.txtLinkedDatabaseName = Nz(.Fields("LinkedDatabaseName").Value, vbNullString)
        Me.txtWorkingPath = .Fields("WorkingPath").Value
        Me.txtDataFileFolder = Nz(.Fields("DataFileFolder").Value, vbNullString)
        Me.txtOutputFolder = Nz(.Fields("OutputFolder").Value, vbNullString)
        Me.txtOutputFilename = Nz(.Fields("OutputFilename").Value, vbNullString)
        Me.txtFileExtension = Nz(.Fields("FileExtension").Value, "xls")
        Me.txtDateFormat = Nz(.Fields("DateFormat").Value, "yyyymmdd")
        Me.chkIsUSFormat = Nz(.Fields("IsUSFormat").Value, False)
        Me.txtArchiveFolder = Nz(.Fields("ArchiveFolder").Value, vbNullString)
        Me.txtSharePointAddress = .Fields("SharePointAddress").Value
        Me.chkSharePointEnabled = Nz(.Fields("SharePointEnabled").Value, False)
        .Close
    End With
    'On affecte les propriétés pour parer aux erreur autour d'un changement d'environnement mal opéré
    If FormLoaded Then
        CurrentEnvironment = Me.EnvironmentID
        SelectedEnvironment = CurrentEnvironment
    End If
    'On affecte la liste déroulante
    Me!cboCurEnvironment = SelectedEnvironment
    'On change le titre
    Me.lblData.Caption = "Les paramètres de l'environnement " & Me.cboCurEnvironment.Column(1) & " :"
    'La fonction est un succès
    GetRecordSource = True
    On Error GoTo 0
    If Not oDB Is Nothing Then oDB.Close

L_ExGetRecordSource:
    Set oRS = Nothing
    Set oDB = Nothing
    Exit Function

L_ErrGetRecordSource:
    'La fonction est un échec
    GetRecordSource = False
    Resume L_ExGetRecordSource
End Function


Private Function NewDataCompleted(ByRef MissedField As String) As Boolean
'---------------------------------------------------------------------------
' Procedure     : NewDataCompleted
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Vérifie que toutes les données sont correctement saisies
'...........................................................................
' Parameters    : MissedField = Nom ou rubrique de la donnée incorrecte

' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim blnAllIsOK                               As Boolean
Dim blnIsInUse                               As Boolean
Dim lngEnvironmentId                         As Long
Dim strEnvironment                           As String
Dim strSharePointAddress                     As String
Dim strLinkedDatabasePath                    As String
Dim strLinkedDatabaseName                    As String
Dim strWorkingPath                           As String
Dim strDataFileFolder                        As String
Dim strArchiveFolder                         As String
Dim strOutputFilename                        As String
Dim strDateFormat                            As String
Dim strFileExtension                         As String
Dim strOutputFolder                          As String
Dim blnSharePointEnabled                     As Boolean
Dim blnIsUSFormat                            As Boolean
    
    'On initialise toutes les variables en fonction du contenu des contrôles (via Nz pour éviter l'erreur 94)
    lngEnvironmentId = Nz(Me.EnvironmentID, vbNullString)
    strEnvironment = Nz(Me.Environment, vbNullString)
    strLinkedDatabasePath = Nz(Me.txtLinkedDatabasePath, vbNullString)
    strLinkedDatabaseName = Nz(Me.txtLinkedDatabaseName, vbNullString)
    strWorkingPath = Nz(Me.txtWorkingPath, vbNullString)
    strDataFileFolder = Nz(Me.txtDataFileFolder, vbNullString)
    strOutputFilename = Nz(Me.txtOutputFilename, vbNullString)
    strFileExtension = Nz(Me.txtFileExtension, vbNullString)
    strDateFormat = Nz(Me.txtDateFormat, vbNullString)
    strSharePointAddress = Nz(Me.txtSharePointAddress, vbNullString)
    strArchiveFolder = Nz(Me.txtArchiveFolder, vbNullString)
    strOutputFolder = Nz(Me.txtOutputFolder, vbNullString)
    blnSharePointEnabled = Nz(Me.chkSharePointEnabled.Value, False)
    blnIsUSFormat = Nz(Me.chkIsUSFormat.Value, False)

    On Error GoTo L_ErrNewDataCompleted
    'On vérifuie une à une chaque variable
    'Si une donnée manque, on lève l'erreur et la fonction retourne False
    blnAllIsOK = False
    If Len(strLinkedDatabasePath) > 7 Then
        If Len(strLinkedDatabaseName) >= 7 Then
            If Len(strWorkingPath) >= 7 Then
                If Len(strDataFileFolder) >= 2 Then
                    If Len(strOutputFilename) >= 2 Then
                        If Len(strFileExtension) >= 3 Then
                            If Len(strDateFormat) >= 8 Then
                                If Len(strArchiveFolder) >= 2 Then
                                    If blnSharePointEnabled Then
                                        If Len(strSharePointAddress) >= 16 Then
                                        Else
                                            MissedField = "Adresse SharePoint"
                                            Me.txtSharePointAddress.SetFocus: txtSharePointAddress.SelLength = Len(strSharePointAddress)
                                            Err.Raise 94
                                        End If
                                    End If
                                Else
                                    MissedField = "Dossier d'archives"
                                    Me.txtArchiveFolder.SetFocus: txtArchiveFolder.SelLength = Len(strArchiveFolder)
                                    Err.Raise 94
                                End If
                            Else
                                MissedField = "Format de la date"
                                Me.txtDateFormat.SetFocus: txtDateFormat.SelLength = Len(strDateFormat)
                                Err.Raise 94
                            End If
                        Else
                            MissedField = "Extension du fichier de sortie"
                            Me.txtFileExtension.SetFocus: txtFileExtension.SelLength = Len(strFileExtension)
                            Err.Raise 94
                        End If
                    Else
                        MissedField = "Nom du fichier de sortie"
                        Me.txtOutputFilename.SetFocus: txtOutputFilename.SelLength = Len(strOutputFilename)
                        Err.Raise 94
                    End If
                Else
                    MissedField = "Dossier des fichiers sources"
                    Me.txtDataFileFolder.SetFocus: txtDataFileFolder.SelLength = Len(strDataFileFolder)
                    Err.Raise 94
                End If
            Else
                MissedField = "Dossier de travail"
                Me.txtWorkingPath.SetFocus: txtWorkingPath.SelLength = Len(strWorkingPath)
                Err.Raise 94
            End If
        Else
            MissedField = "Nom de la base de données"
            Me.txtLinkedDatabaseName.SetFocus: txtLinkedDatabaseName.SelLength = Len(strLinkedDatabaseName)
            Err.Raise 94
        End If
    Else
        MissedField = "Chemin de la base de données"
        Me.txtLinkedDatabasePath.SetFocus: txtLinkedDatabasePath.SelLength = Len(strLinkedDatabasePath)
        Err.Raise 94
    End If
    'Si le site est activé
    If blnSharePointEnabled Then
        'On vérifie la validité de l'URL
        If URLIsValid(strSharePointAddress) = False Then
            'Si elle est invalide... on lève une erreur
            MissedField = "Adresse SharePoint"
            Me.txtSharePointAddress.SetFocus: txtSharePointAddress.SelLength = Len(strSharePointAddress)
            Err.Raise 94
        End If
    End If
    blnAllIsOK = True
    On Error GoTo 0
L_ExNewDataCompleted:
    NewDataCompleted = blnAllIsOK
    Exit Function

L_ErrNewDataCompleted:
    blnAllIsOK = False
    Resume L_ExNewDataCompleted
End Function

Private Sub PasteWebSiteAddress(ByVal TargetTextbox As String, ByVal ItemSubject As String)
'---------------------------------------------------------------------------
' Procedure     : PasteWebSiteAddress
' DateTime      : 11/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Colle l'adresse copié dans le presse-papier
'...........................................................................
' Parameters    : TargetTextbox = Zone de texte cible
'                 ItemSubject = Message d'information sur l'opération
'
' Return Codes  : None
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim strWebAddress                            As String
Dim oCTL                                     As Control

    On Error GoTo L_ErrPastWebSiteAddress
    'Si le presse-papier contient des données
    If GetClipboardData(strWebAddress) = False Then
        'Sinon on lève une erreur
        Err.Raise 13, "Données invalides", "Aucune donnée n'est disponible actuellement dans le Presse-Papier..." & vbCrLf & _
            "Assurez-vous que vous avez sélectionné un lien qui représente l'adresse du site Web SharePoint cible, puis, recommencez..."
    End If
    'Si l'adresse est valide
    If URLIsValid(strWebAddress) = False Then
        'Sinon on lève une erreur
        Err.Raise 13, "Adresse HTTP", ItemSubject & " que vous essayez de coller dans la zone de texte cible n'est pas valide !"
    End If
    'On affecte le contrôle (cas de plusieurs zones d'adresse SharePoint)
    Set oCTL = Me.Controls(TargetTextbox)
    'Le contrôle reçoit l'adresse collée
    oCTL.Value = strWebAddress

    On Error GoTo 0
L_ExPastWebSiteAddress:
    'On libère l'objet
    Set oCTL = Nothing
    Exit Sub
L_ErrPastWebSiteAddress:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExPastWebSiteAddress
End Sub

Explications du déroulement

Le code est commenté ligne par ligne pour une meilleure compréhension ; il est recommandé de bien lire chacune des instructions pour assimiler la philosophie adpotée ici...

Le déroulement reste simple puisqu'il s'axe sur le contenu des champs Textbox pour remplir ou mettre à jour la Table source.
Selon la (seule) valeur possible de la colonne IsInUse, si la table est intégralement remplie, le contenu des champs alimente les contrôles Textbox en fonction de l'environnement choisi et donc l'enregistrement où la valeur du champ IsInUse est True.

Il faut donc choisir l'environnement souhaité pour en changer. Tout remplissage (cas d'une zone de texte vide) ou modification affecte la valeur de la variable de module m_blnDataChanged qui prend la valeur True.
L'événement Timer du formulaire faire clignoter le contrôle Rectangle (Shape) selon la temporisation et indique que l'environnement est actif. L'état de la variable m_blnDataChanged active ou désactive alors le bouton de sauvegarde selon le cas.

Vous-même ou l'utilisateur en charge de l'administration de la base de données peut alors remplir ou mettre à jour alors les éléments constitutifs pour chacun des environnements.
Selon l'environnement choisi, l'ensemble des tables liées de la base de données dorsale spécifiée est réattaché dynamiquement à l'application.
L'ensemble du code du projet, si peu que vous ayez adapté les fonctions et procédures du projet selon l'environnement, s'exécutera en conséquence...

Normalement et en dehors des deux chemins de la base de données dorsale et de travail et l'eventuelle adresse SharePoint, tous les dossiers doivent porter le même nom ; d'une part c'est plus souple et d'autre par c'est plus simple à définir.
Il n'est pas recommandé de définir des noms de dossier avec un suffixe Test ou UAT ou Prod car cela vous sortirait du contexte de la réalité. Mais si vous pensez que c'est mieux de procéder ainsi, pourquoi pas.

3-1-7. Les modules externes complémentaires

Certaines fonctions et procédures du formulaire font appel à des fonctions qui ne font pas partie intégrante de son module de classe en cours de réalisation. Et pour cause, ces fonctions sont considérées comme publiques et donc partagées. Elles peuvent très bien, pour certaines d'entre-elles, avoir déjà été developpées au sein de votre projet...
Si tel était le cas, il est fort probable que vous ayez des adapations à mettre en oeuvre.
N'importe comment, dans un premier temps, créez le module et l'ensemble des fonctions qui l'accompagnent et vous verrez par la suite comment vous organiser pour fusionner les éventuelles fonctions qui remplissent le même rôle si vous avez des fonctions en double.

Vous ajouterez donc un module dédié au sein de votre projet ou bien dans un nouveau projet selon la situation...
Vous y écrirez l'ensemble des fonctions stipulées ci-après.

L'en-tête comporte des déclarations d'API utiles aux fonctions principalement orientées à l'accès aux fichiers et aux dossiers du système. La structure OPENFILENAME est attachée à le fonction dédiée à la sélection d'un fichier. Les deux constantes sont pour l'une la valeur flag définissant si l'environnement est réservé au développeur et pour l'autre la requête SQL permettant de sélectionner l'environnement actif...

Code du module basApplication - En-tête du module (déclarations)
Sélectionnez
'**********************************************************************
' Module            : basApplication
' Type              : Module
' DateTime          : 28/11/2012
' Author            : Jean-Philippe AMBROSINO
' Review            : Jean-Philippe AMBROSINO
' Purpose           : Module used to share functions between modules
'
''**********************************************************************

Option Compare Database
Option Explicit

'Constante de mode d'ouverture pour ShellExecute
Private Const SW_SHOWNORMAL                  As Integer = 1
'API permettant d'exécuter différentes opérations selon lpOperation
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal HWnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
'API permettant d'interrompre le temps en millisecondes
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
'API permettant de greffer un \ sur un chemin
Private Declare Function PathAddBackslash Lib "shlwapi.dll" Alias "PathAddBackslashA" (ByVal pszPath As String) As Long
'API peremttant de vérifier qu'un chemin est une URL
Private Declare Function PathIsURL Lib "shlwapi.dll" Alias "PathIsURLA" (ByVal pszPath As String) As Long
'API permettant d'ouvrir la boîte de dialogue des fichiers
Private Declare Function GetOpenFileName Lib "comdlg32.dll" Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long
'Structure OpenFileName
Private Type OPENFILENAME
    lStructSize As Long
    hwndOwner As Long
    hInstance As Long
    lpstrFilter As String
    lpstrCustomFilter As String
    nMaxCustFilter As Long
    nFilterIndex As Long
    lpstrFile As String
    nMaxFile As Long
    lpstrFileTitle As String
    nMaxFileTitle As Long
    lpstrInitialDir As String
    lpstrTitle As String
    flags As Long
    nFileOffset As Integer
    nFileExtension As Integer
    lpstrDefExt As String
    lCustData As Long
    lpfnHook As Long
    lpTemplateName As String
End Type
'Enumération des 3 environnements possibles
Public Enum g_eEnvironment
    EnvironmentTest = 1
    EnvironmentUAT = 2
    EnvironmentProd = 3
End Enum
'Constante définissant si l'environnement est réservé au développeur pour ses tests
Public Const RESERVED_TO_DEVELOPER                  As Boolean = True
'Requête par défaut de la sélection de l'environnement actif
Private Const SQL_ENV                                As String = "SELECT EnvironmentID FROM tblProcessEnvironment WHERE IsInUse = True AND ReservedToDeveloper = " & RESERVED_TO_DEVELOPER & ";"


La fonction GetCurrentEnvironment() permet de retourner l'ID de l'environnement actif.
Elle s'exécute en fonction de la requête définie en constante.

Code du module basApplication - Fonction GetCurrentEnvironment()
Sélectionnez
Public Function GetCurrentEnvironment() As g_eEnvironment
'---------------------------------------------------------------------------
' Procedure     : GetCurrentEnvironment
' DateTime      : 01/11/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Retourne l'environnement actif
'...........................................................................
' Parameters    : None

' Return Codes  : g_eEnvironment
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim oRS                                      As DAO.Recordset

    On Error GoTo L_ErrGetCurrentEnvironment

    Set oRS = CurrentDb.OpenRecordset(SQL_ENV, dbOpenSnapshot)
    With oRS
        If Not oRS.EOF Then
            GetCurrentEnvironment = Nz(.Fields(0).Value, 0)
        End If
        .Close
    End With

    On Error GoTo 0
L_ExGetCurrentEnvironment:
    Set oRS = Nothing
    Exit Function

L_ErrGetCurrentEnvironment:
    GetCurrentEnvironment = 0
    Resume L_ExGetCurrentEnvironment
End Function


La fonction GetPathName() extrait le chemin d'accès d'un chemin complet.

Code du module basApplication - Fonction GetPathName()
Sélectionnez
Function GetPathName(ByVal FullPath As String, ByVal KeepBackSlash As Boolean) As String
'---------------------------------------------------------------------------
' Procedure     : GetPathName
' DateTime      : 10/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Chemin extrait d'un chemin complet
'...........................................................................
' Parameters    : FullPath = Chemin complet
'                 KeepBackSlash = True si on conserve le \

' Return Codes  : String = Pathname
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

    GetPathName = Left$(FullPath, InStrRev(FullPath, "\") - IIf(KeepBackSlash, 0, 1))
End Function


La fonction GetFileName() extrait le nom du fichier d'un chemin complet.

Code du module basApplication - Fonction GetFileName()
Sélectionnez
Function GetFileName(ByVal FullFile As String) As String
'---------------------------------------------------------------------------
' Procedure     : GetFileName
' DateTime      : 10/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Fichier extrait d'un chemin complet
'...........................................................................
' Parameters    : FullFile = Chemin complet

' Return Codes  : String = Filename
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

    GetFileName = Mid$(FullFile, InStrRev(FullFile, "\") + 1)
End Function


La fonction AddDirSeparator() permet d'ajouter un séparateur de chemin s'il n'y en a pas.

Code du module basApplication - Fonction AddDirSeparator()
Sélectionnez
Function AddDirSeparator(ByVal TargetPath As String) As String
'---------------------------------------------------------------------------
' Procedure     : AddDirSeparator
' DateTime      : 10/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Ajoute un \ au chemin
'...........................................................................
' Parameters    : TargetPath = chein visé

' Return Codes  : String = TargetPath\
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim strPath                                  As String
    strPath = TargetPath & vbNullChar
    PathAddBackslash strPath
    AddDirSeparator = StripTerminator(strPath)
End Function


La fonction StripTerminator() permet de supprimer la partie droite d'une chaîne où se trouvent des caractères 0 soit via la valeur Chr(0) ou via la constante vbNullChar souvent présents en fin de chaîne retournée par les API comme justement la fonction GetOpenFileName().

Code du module basApplication - Fonction StripTerminator()
Sélectionnez
Function StripTerminator(ByVal InputString As String) As String
'---------------------------------------------------------------------------
' Procedure     : StripTerminator
' DateTime      : 10/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Supprime le Chr(0) de la chaîne InputString
'...........................................................................
' Parameters    : InputString = valeur concerné

' Return Codes  : String = InputString
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim ZeroPos                                  As Long
    ZeroPos = InStr(1, InputString, vbNullChar, 0)
    If ZeroPos > 0 Then
        StripTerminator = Left$(InputString, ZeroPos - 1)
    Else
        StripTerminator = InputString
    End If
End Function


Le fonction GetLinkedDatabasePath() permet de renvoyer le chemin de la base de données dorsale en s'appuyant sur le chaîne retournée par la propriété Connect d'une table liée.

Code du module basApplication - Fonction GetLinkedDatabasePath()
Sélectionnez
Public Function GetLinkedDatabasePath(ByRef DB As DAO.Database, ByVal LinkedTableName As String, ByVal WithBackSlash As Boolean) As String
'---------------------------------------------------------------------------
' Procedure     : GetLinkedDatabasePath
' DateTime      : 10/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Renvoie le chemin de la BDD dorsale
'...........................................................................
' Parameters    : DB = Objet Database
'                 LinkedTableName = Nom de la table liée
'                 WithBackSlash = True si on conserve le \

' Return Codes  : String = Chemin de la BDD avec ou sans \
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim strDBPath                                As String
    strDBPath = Mid$(DB.TableDefs(LinkedTableName).Connect, 11)
    strDBPath = Mid$(strDBPath, InStrRev(strDBPath, " = ") + 1)
    GetLinkedDatabasePath = Left$(strDBPath, InStrRev(strDBPath, "\") - IIf(WithBackSlash, 0, 1))
End Function


La fonction ShellOpenExec() exploite l'API ShellExecute() pour effectuer une opération sur des fichiers qu'il soient exécutables ou document.
On utilise cette fonction pour :
- soit ouvrir un document via l'exécutable correspondant identifié :
    . par l'association du fichier ;
    . par l'usage de l'API FindExecutable() ;
- soit imprimer un fichier ;
- soit ouvrir une page WEB.

Code du module basApplication - Fonction ShellOpenExec()
Sélectionnez
Public Function ShellOpenExec(ByVal ApplicationPath As String, Optional ByVal _
    HWnd As Long = 0, Optional ByVal Parameters As String = vbNullString, Optional _
    ByVal Operation As String = "open", Optional ByRef ShellError As String) As Long
'---------------------------------------------------------------------------
' Procedure     : ShellOpenExec
' DateTime      : 10/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Effectue une execution sur fichier selon l'opération souhaité
'...........................................................................
' Parameters    : ApplicationPath = Fichier ou Dossier
'                 HWnd = Handle de l'application cible
'                 Parameters = Paramètres passés
'                 Operation = Quelle opération à effectuer
'                 ShellError = Message retourné si erreur

' Return Codes  : Long > 32 si succès
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

Dim lngReturn                                As Long
    On Error GoTo L_ErrShellOpenExec

    lngReturn = ShellExecute(HWnd, Operation, ApplicationPath, Parameters, "C:\", SW_SHOWNORMAL)

    On Error GoTo 0
L_ExShellOpenExec:
    Exit Function

L_ErrShellOpenExec:
    ShellError = "(" & Err.Number & ")" & Err.Description
    Resume L_ExShellOpenExec
End Function


La fonction CheckFolderName() permet de retourner la valeur ASCII passée en paramètre à la procédure événementielle KeyPress() appelante et retourne zéro (0) si elle ne correspond pas à la plage que j'ai définie dans le Select Case.
Elle exclut en résumé tous les caractères interdits et notamment les caractères spéciaux.

Code du module basApplication - Fonction CheckFolderName()
Sélectionnez
Public Function CheckFolderName(ByVal KeyAscii As Integer, Optional ByVal IsURL As Boolean = False) As Integer
'---------------------------------------------------------------------------
' Procedure     : CheckFolderName
' DateTime      : 10/10/2010
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Retourne le carctère autorisé lors de la saisie d'un chemin ou d'une adresse
'...........................................................................
' Parameters    : KeyAscii = ASCII Char
'                 IsURL = Vrai pour contrôler les URL

' Return Codes  : Integer > 0 si succès
'...........................................................................
' Notes         :
'---------------------------------------------------------------------------

    If IsURL Then
        Select Case KeyAscii
            Case 8, 32, 45 To 58, 65 To 90, 95, 97 To 122: CheckFolderName = KeyAscii
            Case Else: Beep: CheckFolderName = 0
        End Select
    Else
        Select Case KeyAscii
            Case 8, 32, 45, 48 To 57, 65 To 90, 95, 97 To 122: CheckFolderName  = KeyAscii
            Case Else: Beep: CheckFolderName = 0
        End Select
    End If
End Function


La fonction SelectAnyFile() exploite l'API GetOpenFileName() pour pouvoir sélectionner un fichier à partir de la boîte de dialogue commune Ouvrir.
La fonction aboutie si un fichier est sélectionné et la chaîne en retour représente le chemin complet du fichier ; cette dernière se voit traversée par la fonction StripTerminator() vue plus haut.

Code du module basApplication - Fonction SelectAnyFile()
Sélectionnez
Public Function SelectAnyFile(ByVal HWnd As Long, Optional Title As String, Optional StartFolderOrDrive As String) As String
'---------------------------------------------------------------------------
' Procedure     : SelectAnyFile
' DateTime      : 28/11/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Returns
'...........................................................................
' Parameters    : HWnd = handle de l'appelant
'                 Title = Titre de la fenêtre
'                 StartFolderOrDrive = Dossier de démarrage

' Return Codes  : String = Nom complet du fichier
'...........................................................................
' Notes         : Appelle GetOpenFilename()
'---------------------------------------------------------------------------

Const FILE_FILTER                            As String = "Base de données Microsoft Access (2007)" & vbNullChar & "*.accdb" & vbNullChar _
    & "Base de données Microsoft Access (2003)" & vbNullChar & "*.mdb" & vbNullChar
Dim tyOFN                                    As OPENFILENAME

    With tyOFN
        .lStructSize = Len(tyOFN)
        .hwndOwner = HWnd
        .hInstance = Application.hWndAccessApp
        .lpstrFilter = FILE_FILTER
        .lpstrFile = Space$(254)
        .nMaxFile = 255
        .lpstrFileTitle = Space$(254)
        .nMaxFileTitle = 255
        .lpstrInitialDir = StartFolderOrDrive
        .lpstrTitle = Title
        .flags = 0
        If GetOpenFileName(tyOFN) Then
            SelectAnyFile = StripTerminator(.lpstrFile)
        End If
    End With
End Function


La fonction SelectAnyFolder() exploite l'API Shell32() pour pouvoir sélectionner un dossier à partir d'une boîte de dialogue.

Code du module basApplication - Fonction SelectAnyFolder()
Sélectionnez
Public Function SelectAnyFolder(Optional Title As String, Optional StartFolderOrDrive As String) As String
'---------------------------------------------------------------------------
' Procedure     : SelectAnyFolder
' DateTime      : 10/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet de sélectionner un dossier
'...........................................................................
' Parameters    : Title = Titre de la fenêtre
'                 StartFolderOrDrive = Dossier de départ : par défaut 'Ordinateur' ou 'My computer'
'
' Return Codes  : String = Dossier sélectionné
'...........................................................................
' Notes         : Faire référence à Microsoft Shell Controls And Automation
'                 Plusieurs autre solutions existent : BrowseForFolder, MSO Dialog,...etc.
'                 Shell32 options : 1 dossiers seuls ; 16384 dossiers et fichiers
'---------------------------------------------------------------------------
Dim oShell                                   As Shell32.Shell
Dim oFolder                                  As Shell32.Folder

    On Error GoTo L_ErrSelectAnyFolder

    Set oShell = New Shell32.Shell
    Set oFolder = oShell.BrowseForFolder(0, Title, 1, StartFolderOrDrive)
    If Not oFolder Is Nothing Then
        SelectAnyFolder = oFolder.Items.Item.Path
    End If
    Set oFolder = Nothing
    Set oShell = Nothing

    On Error GoTo 0
L_ExSelectAnyFolder:
    Exit Function

L_ErrSelectAnyFolder:
    MsgBox Err.Description, vbExclamation, Err.Source
    Resume L_ExSelectAnyFolder
End Function


La fonction ThisFolderExists() permet de vérifier via un FileSystemObject l'existence d'un dossier.

Code du module basApplication - Fonction ThisFolderExists()
Sélectionnez
Public Function ThisFolderExists(ByVal FolderName As String) As Boolean
'---------------------------------------------------------------------------
' Procedure     : ThisFolderExists
' DateTime      : 24/06/2007
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Vérifie l'existence d'un dossier
'...........................................................................
' Parameters    : FolderName = Nom du dossier à vérifier
' Return Codes  : Boolean = True si succès
'...........................................................................
' Notes         : Faire référence à scrrun.dll
'---------------------------------------------------------------------------

Dim oFSO                                     As Scripting.FileSystemObject

    On Error GoTo ThisFolderExists_Error

    Set oFSO = New Scripting.FileSystemObject
    ThisFolderExists = (oFSO.FolderExists(FolderName))
    On Error GoTo 0
ThisFolderExists_Exit:
    Set oFSO = Nothing
    Exit Function

ThisFolderExists_Error:
    ThisFolderExists = False
    Resume ThisFolderExists_Exit
End Function


La fonction ThisFileExists() permet de vérifier via un FileSystemObject l'existence d'un fichier.

Code du module basApplication - Fonction ThisFileExists()
Sélectionnez
Public Function ThisFileExists(ByVal FileName As String) As Boolean
'---------------------------------------------------------------------------
' Procedure     : ThisFileExists
' DateTime      : 24/06/2007
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Vérifie l'existence d'un fichier
'...........................................................................
' Parameters    : FileName = Nom du fichier à vérifier
' Return Codes  : Boolean = True si succès
'...........................................................................
' Notes         : Faire référence à scrrun.dll
'---------------------------------------------------------------------------

Dim oFSO                                     As Scripting.FileSystemObject

    On Error GoTo ThisFileExists_Error

    Set oFSO = New Scripting.FileSystemObject
    ThisFileExists = (oFSO.FileExists(FileName))
    On Error GoTo 0
ThisFileExists_Exit:
    Set oFSO = Nothing
    Exit Function

ThisFileExists_Error:
    ThisFileExists = False
    Resume ThisFileExists_Exit
End Function


La fonction IsSharePointURLIsAvailable() n'est ici pas écrite. Initialement*, la fonction doit tenter de déposer un fichier dans un DocumentLibrary5 sur le site passé dans le paramètre URL, et le détruire une fois le dépôt réussi et ce, dans le seul but de détecter que l'opération est possible.
J'ai intentionnellement forcé la valeur True pour l'application théorique du tutoriel.

Code du module basApplication - Fonction IsSharePointURLIsAvailable()
Sélectionnez
Public Function IsSharePointURLIsAvailable(ByVal URL As String) As Boolean
'---------------------------------------------------------------------------
' Procedure     : IsSharePointURLIsAvailable
' DateTime      : 10/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet de contrôler qu'une adresse web est accessible
'...........................................................................
' Parameters    : URL = adresse à contrôler

' Return Codes  : Boolean = True if so success
'...........................................................................
' Notes         : Fonction à écrire
'---------------------------------------------------------------------------

    On Error GoTo L_ErrIsSharePointURLIsAvailable
    '[....]
    IsSharePointURLIsAvailable = True
    On Error GoTo 0
L_ExIsSharePointURLIsAvailable:
    Exit Function

L_ErrIsSharePointURLIsAvailable:
    IsSharePointURLIsAvailable = False
    Resume L_ExIsSharePointURLIsAvailable
End Function


La fonction URLIsValid() permet de vérifier la syntaxe d'une adresse Web. Elle exploite pour se faire l'API PathIsURL().

Code du module basApplication - Fonction URLIsValid()
Sélectionnez
Public Function URLIsValid(ByVal URL As String) As Boolean
'---------------------------------------------------------------------------
' Procedure     : URLIsValid
' DateTime      : 10/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Permet de contrôler qu'une adresse web est valide
'...........................................................................
' Parameters    : URL = adresse à contrôler

' Return Codes  : Boolean = True if so success
'...........................................................................
' Notes         : Exploite PathIsURL pour contrôler l'adresse
'---------------------------------------------------------------------------

    On Error GoTo L_ErrURLIsValid
    URLIsValid = CBool(PathIsURL(URL))
    On Error GoTo 0
L_ExURLIsValid:
    Exit Function

L_ErrURLIsValid:
    URLIsValid = False
    Resume L_ExURLIsValid
End Function


La fonction GetClipboardData() permet de vérifier que le presse-papier contient une chaîne de caractères.

Code du module basApplication - Fonction GetClipboardData()
Sélectionnez
Public Function GetClipboardData(Optional ByRef Data As String) As Boolean
'---------------------------------------------------------------------------
' Procedure     : GetClipboardData
' DateTime      : 10/10/2012
' Author        : Jean-Philippe AMBROSINO
' Purpose       : Vérifie si le Press-Papier n'est pas vide
'...........................................................................
' Parameters    : [Data] = Contenu du Press Papier si nécessaire

' Return Codes  : Boolean = True si succès
'...........................................................................
' Notes         : Faire référence à FM20.dll
'---------------------------------------------------------------------------
Dim oData                                    As DataObject

    Set oData = New DataObject
    oData.GetFromClipboard
    Data = oData.GetText
    GetClipboardData = (Len(Data) >= 1)
    Set oData = Nothing
End Function


La fonction ChangeLinkedDatabase() pemret de réattacher toutes les tables de la base de données dorsale liée à l'application.

Code du module basApplication - Fonction ChangeLinkedDatabase()
Sélectionnez
Public Function ChangeLinkedDatabase(ByVal TargetDatabasePath As String, ByVal TargetDatabaseName As String, Optional ByVal Pwd As String) As Boolean
Dim oDBApp                                   As DAO.Database
Dim oDBBackEnd                               As DAO.Database
Dim oTDF                                     As DAO.TableDef

Dim strTargetTableName                       As String
Dim strSourceTableName                       As String
Dim strLinkTemp                              As String

Dim strBackEndDBName As String

    'Défini le gestionnaire d'erreurs
    On Error GoTo L_ErrfBlnAttacherBase
    'Désactive les messages système.
    DoCmd.SetWarnings False
    'Change le curseur en sablier
    DoCmd.Hourglass True
    'Affecte la BDD courante à l'objet Database
    Set oDBApp = CurrentDb
    'Construction de chemin complet
    strBackEndDBName = AddDirSeparator(TargetDatabasePath) & TargetDatabaseName
    'Si toutefois il y a un mot de passe...
    If Len(Pwd) Is Nothing Then
        'On ouvre avec le mot de passe
        Set oDBBackEnd = DBEngine.OpenDatabase(strBackEndDBName, False, False, "MS Access;PWD=" & Pwd)
    Else
        'Sinon on ouvre simplement
        Set oDBBackEnd = DBEngine.OpenDatabase(strBackEndDBName, False, False)
    End If
    'On referme la base de données Dorsale qui n'est plus utilisé dans l'état
    If Not oDBBackEnd Is Nothing Then oDBBackEnd.Close
   'Ensuite pour chacune des tables de la BDD
    For Each oTDF In oDBApp.TableDefs
        'Avec la table...
        With oTDF
            'Si elle n'est pas système et qu'elle est attachée
            If .Attributes = dbAttachedTable And Not .Attributes = dbSystemObject Then
                'On affecte la chaîne de connexion à la variable
                strLinkTemp = oTDF.Connect
                'Si dans la variable strLinkTemp se trouve le nom et le chemin de la base de données visée
                If InStr(1, strLinkTemp, TargetDatabaseName, vbTextCompare) > 1 _
                    And InStr(1, strLinkTemp, TargetDatabasePath, vbTextCompare) > 1 Then
                    'On affecte alors le nom de la table à la variable
                    strSourceTableName = oTDF.Name
                    'On supprimer alors la table liée de l'application (l'objet doit exister)
                    DoCmd.DeleteObject acTable, strSourceTableName
                    'On égalise le nom de la table source et celle de destination
                    strTargetTableName = strSourceTableName
                    'On procède à l'attachement de la table dans l'application
                    DoCmd.TransferDatabase acLink, "Microsoft Access", strBackEndDBName, acTable, strSourceTableName, strTargetTableName
                End If
            End If
        End With
        'On passe à la table suivante
    Next oTDF
    'Si l'objet Database de l'application est valide, on ferme la BDD
    If Not oDBApp Is Nothing Then oDBApp.Close
    'Si l'objet Database de la BDD Dorsale est valide, on ferme la BDD
    ChangeLinkedDatabase = True
    On Error GoTo 0
L_ExfBlnAttacherBase:
    'On libère tous les objets de la mémoire
    Set oTDF = Nothing
    Set oDBApp = Nothing
    Set oDBBackEnd = Nothing
    'On restitue le cursor dans son aspect par défaut...
    DoCmd.Hourglass False
    Exit Function
L_ErrfBlnAttacherBase:
    'La fonction a échoué
    ChangeLinkedDatabase = False
    'Un message décrit l'erreur
    MsgBox Err.Description, vbExclamation, Err.Source
    'On reprend pour sortir de la fonction
    Resume L_ExfBlnAttacherBase
End Function


Le module se termine ici.
Ainsi que dit plus haut, il probable que votre projet comporte une ou plusieurs des fonctions exposées ici.

3-1-8. Affectation du comportement des procédures et fonctions du projet selon l'environnement

Il ne suffit donc pas de changer d'environnement pour que tout se passe comme vous l'entendez...
En effet, le but de cette définition dynamique de l'environnement consiste de prime abord à tester des nouvelles fonctionnalités ou des modifications apportées à tel ou tel objet, qu'il soit formulaire, état, requête dynamique ou module de classe...
Au sein de votre code doit alors être élaboré des blocs conditionnels typiques afin de faire en sorte que le code s'exécute de la façon dont l'entend l'environnement sélectionné.

Imaginez par exemple que vous ajoutiez une nouvelle table qui est en relation avec une table existante en production et que du code spécifique doive être greffé autour de cette définition de table et de son contenu pour proposer à l'utilisateur de nouvelles données. Il semble alors évident que cette nouvelle table se trouve alors dans la base de données Dorsale de Test, puis celle de Recette (UAT1).
Votre code doit alors considérer l'existence de cette table, mais dans l'environnement de Production, elle n'existe pas encore.
Il vous faut donc rédiger le code en conséquence...

Dans un contexte supposé réel, voici un exemple. Initialement, un ancien développeur avait développé une fonction permettant de calculer l'âge d'une personne avec une fonction qui retournait un entier et donc, il avait été contraint de bricoler des chaînes de caractères afin de greffer à l'âge retourné, le mot an ou ans avec un s selon que la personne avait un an ou plus et donc, accorder le pluriel dans les différentes zones de texte des formulaires faisant appel à cette fonction...
Vous, en charge de faire évoluer l'application, avez donc décidé de créer une nouvelle fonction Age() qui se charge en une fois de retourner l'âge textuel au singulier ou au pluriel selon le cas.
Vous avez donc réécrit la fonction sans altérer le code existant...
Débordant d'initiative, vous avez fait en sorte que cette fonction puisse calculer un âge selon une date autre que celle du jour comme date de référence ce qui augmente la puissance de cette nouvelle fonction...

Exemple d'application d'un bloc de code selon l'environnement
Sélectionnez
Option Compare Database
Option Explicit
 
'On inscrit en dur l'énumération des 3 environnements uniquement pour la démo
Private Enum m_eEnvironment
    envTest = 1
    envUAT = 2
    envProd = 4
End Enum

'Variable définissant l'environnement pour l'ensemble du projet
'Préférez une propriété au sein d'une classe plutôt qu'une variable publique.
Public g_intExcecMode As m_eEnvironment

Sub Main()
'On force l'environnement en Test
'(bien entendu, cet environnement doit être puisé dans la table en fonction de son état actif ou non)
    g_intExcecMode = envTest
    'On cherche à calculer l'âge d'une personne selon une date
    MsgBox QuelAge(#6/9/1966#)
End Sub

Private Function QuelAge(ByVal DateValue As Date, Optional ByVal DateRef As Date) As String
Dim strAge                                   As String
Dim intAge                                   As Integer

    'On vérifie qu'une adte est bien passée en paramètre
    If Not IsDate(DateRef) Or CLng(DateRef) = 0 Then
        DateRef = Now()
    End If
    'On retourne l'âge en fonction de l'environnement
    Select Case g_intExcecMode
        Case envTest
            'La nouvelle fonction est complète
            strAge = AgeNewVersion(DateRef, DateValue, True)
         Case envUAT, envProd
            'On appelle la fonction originale
            intAge = Age(DateValue)
            'On bricole avec un IIf pour construire la chaîne
            strAge = Trim$(Str(intAge)) & IIf(intAge = 1, " an", " ans")
    End Select
    QuelAge = strAge
End Function

Private Function Age(ByVal DateValue As Date) As Variant
'L'ancienne fonction qui retourne l'âge en entier
    Age = (Now - DateValue) \ 365.25
End Function

Private Function AgeNewVersion(ByVal DateValue As Date, ByVal DateRef As Date, Optional ByVal FullSentence As Boolean) As String
'La nouvelle fonction qui retourne l'âge en texte
Dim lngResult                                As Long
    lngResult = (DateValue - DateRef) \ 365.25
    AgeNewVersion = lngResult & IIf(FullSentence, " an" & IIf(lngResult <= 1, vbNullString, "s"), vbNullString)
End Function

Bon, soyez indulgent, ce n'est pas le super exemple par définition...
J'ai écrit ce bloc de code pour vous montrer un peu l'utilité et la mise en application de la gestion de l'environnement.
En réalité, la mise en oeuvre est beaucoup plus fine et donc un peu plus complexe que cela.
La surcharge n'existant pas en Visual basic, il n'est pas possible de créer une nouvelle fonction Age() avec les deux paramètres que j'ai ajoutés pour la circonstance.

Toutefois, il est possible de créer une fonction avec le même nom et des paramètres différents, mais dans deux modules séparés... Dans ce cas, si vous souhaitez l'appeler, il vous faudra qualifier son nom avec celui du module...

Exemple d'application du même bloc de code avec une pseudo surcharge
Sélectionnez
Option Compare Database
Option Explicit
 
'On inscrit en dur l'énumération des 3 environnements
Private Enum m_eEnvironment
    envTest = 1
    envUAT = 2
    envProd = 3
End Enum

'Variable définissant l'environnement pour l'ensemble du projet
'Préférez une propriété au sein d'une classe plutôt qu'une variable publique.
Public g_intExcecMode As m_eEnvironment

Sub Main()
'On force l'environnement en Test
'(bien entendu, cet environnement doit être puisé dans la table en fonction de son état actif ou non)
    g_intExcecMode = 3
    'On cherche à calculer l'âge d'une personne selon une date
    MsgBox QuelAge(#6/9/1966#)
End Sub

Private Function QuelAge(ByVal DateValue As Date, Optional ByVal DateRef As Date) As String
Dim strAge                                   As String
Dim intAge                                   As Integer

    'On vérifie qu'une adte est bien passée en paramètre
    If Not IsDate(DateRef) Or CLng(DateRef) = 0 Then
        DateRef = Now()
    End If
    'On retourne l'âge en fonction de l'environnement
    Select Case g_intExcecMode
        Case envTest
            'On appelle la nouvelle fonction Age()
            strAge = basNewFunctions.Age(DateRef, DateValue, True)
        Case envUAT
            'Pas déployé
            strAge = "Non disponible"
        Case envProd
            'On appelle la fonction originale
            intAge = basFunctions.Age(DateValue)
            'On bricole avec un IIf pour construire la chaîne
            strAge = Trim$(Str(intAge)) & IIf(intAge = 1, " an", " ans")
    End Select
    QuelAge = strAge
End Function

Remarquez la qualification de l'appel de la fonction Age() par les modules "basNewFunctions" pour le nouvel environnement de Test ou plus exactement celui initialisé dans la variable g_intExcecMode et le module basFunctions pour l'environnement de Production.

Les modules contiennent donc respectivement chacun une fonction Age().
La différence réside dans le fait que la nouvelle fonction est plus générique et possède plus d'arguments.
Bien que cela ne puisse pas s'assimiler comme une fonction surchargée2, il s'avère bien pratique d'avoir recours à ce type de méthode pour employer un même nom de fonction qui ne retourne pas le même type de résultat ou bien qui se comporte différemment du fait notamment des paramètres qui lui auraient été greffés.

Contenu du module basFunctions
Sélectionnez
Option Compare Database
Option Explicit

Public Function Age(ByVal DateValue As Date) As Integer
'L'ancienne fonction qui retourne l'âge en entier
    Age = (Now - DateValue) \ 365.25
End Function
Contenu du module basNewFunctions
Sélectionnez
Option Compare Database
Option Explicit
 
Public Function Age(ByVal DateValue As Date, ByVal DateRef As Date, Optional ByVal FullSentence As Boolean) As String
'La nouvelle fonction qui retourne l'âge en texte
Dim lngResult                                As Long
    lngResult = (DateValue - DateRef) \ 365.25
    Age = lngResult & IIf(FullSentence, " an" & IIf(lngResult <= 1, vbNullString, "s"), vbNullString)
End Function

En complément...

Un peu plus bas, j'aborde avec ce phénomène un autre cas plus typique et qui attaque cette fois le comportement des contrôles...

4. Mise en exécution

Une fois le code mis en place, vous basculez de nouveau sur votre formulaire (Alt+Tab) ou bien vous quittez l'éditeur Visual Basic depuis le menu Fichier.

4-1. Définition de l'environnement

Je sous-entends par "définition de l'environnement" le procédé qui va vous permettre de faire en sorte que votre application se comporte et agisse en fonction de celui que vous avez sélectionné. Il s'agit alors de mettre en exécution les différents processus tout en impactant la base de données visée dorsale d'une part et les opérations de traitement des fichiers en rapport d'autre part si l'on reste dans le contexte de ce tutoriel.
En effet, une mise en test dans un premier temps vous permet de faire vos propres évaluations et c'est donc vous et vous seul qui décidez lorsque vous pouvez ou non délivrer l'application pour une mise en recette... Une fois la mise recette (UAT1) validée, seul le ou les utilisaeurs pourront décidez lorsque vous pourrez lancer la version de production...

Mise en situation exemple

Imaginons par exemple que votre projet est en production et tourne parfaitement bien (oui, oui, ça arrive)...
Des besoins spécifiques d'un autre service contraint de traiter des données annexes à celles déjà traitées par votre application vous parviennent avec les spécifications fonctionnelles en rapports - du moins, il vaut mieux que ce soit écrit que verbal, je sais de quoi je parle ;o) -
Dans la sitatution actuelle, ces données se trouvent dans un des fichiers sources importés dans votre projet où justement certaines colonnes ne sont pas importées ou traitées par votre processus de traitement et ce sont justement quelques-unes d'entre elles dont ce service a besoin.
Il va donc être nécessaire de modifier :

  • les scripts d'importation,
  • le traitement sur les données qui en découle,
  • la modification de la structure des tables de destintion et les éventuelles requêtes,
  • la création des processus d'export pour honorer le besoin du dit service
  • etc...

Pour mettre cela en application sans perturber le fonctionnement de la production, vous pourrez alors une fois tout mis en œuvre, basculer un ou des utilisateurs dans un environnement de "recette" pour apprécier vos modifications ou nouveautés et ainsi, vérifier le comportement de non-régression de votre projet sans altérer les données de production déjà en base.
En effet, toute modification même contrôlée par vous peut générer des erreurs inattendues lorsque l'application est entre les mains d'autres utilisateurs...

4-2. Le formulaire de définition de l'environnement

Ce formulaire est ici le reflet de la structure de la table. Il vous appartient alors de faire en sorte que le nombre de contrôles utiles à votre projet soit effectivement ajusté en conséquence. Dans le cas présenté ici, le minimum a été prévu notamment en matière de sous-dossiers cibles. Dans mon projet initial qui a servi d'idée pour ce tutoriel, le nombre était largement doublé.

L'exemple étudié ici considère que le chemin principal est la racine de l'ensemble des sous-dossiers. Ainsi, si tel n'était pas le cas pour vous, une adaptation serait nécessaire.
En parallèle à cela, il a été stipulé qu'une seule adresse SharePoint. Là aussi, il n'est pas utile de la considérer si toutefois aucun site SharePoint n'existe pour votre projet.

4-3. Validation de la définition de l'environnement

Le processus de définition de l'environnement va tourner autour d'un jeu de fonctions faisant partie intégrante de la classe de formulaire correspondant, mais aussi des fonctions externes à la classe, celles-ci pouvant déjà exister dans votre projet ou bien pouvant avoir à être utilisées dans un autre contexte depuis un autre formulaire par exemple. Le principe de remplissage est simple :

  • La sélection du fichier représentant la base de données dorsale
  • la sélection du chemin racine est effectuée par sélection du dossier
  • la sélection ou la saisie manuelle des sous-dossiers avec contrôle de cohérence de caractères acceptables
  • la saisie manuelle du nom du fichier de sortie et de son extension
  • la saisie manuelle ou le collage de l'adresse du site SharePoint avec contrôle de cohérence de caractères acceptables
  • les annulations ou mauvaises manipulations
  • et enfin la validation

4-3-1. Principe de validation

Pour que l'environnement soit accepté, toutes les données de base doivent être correctes.
- La base de données dorsale doit exister
- Le chemin de travail doit être disponible
- Dans le contexte de ce tutoriel, seuls les dossiers ne sont pas vérifiés dans leur existence en tant que dossier, car (dans mon projet*) ils seront (étaient*) créés sur le chemin de travail dès que les processus d'importation ou d'exportation seront (étaient*) exécutés (code non exposé ici*).
En revanche, leur stipulation dans les zones de texte est obligatoire.
Si la case à cocher du site SharePoint est cochée, c'est que vous avez décrété qu'un site est disponible et donc, que l'adresse de ce site doit être valide...

Sauvegarde effective de l'environnement

Une fois toutes les zones de texte alimentées, vous cliquez sur le bouton de sauvegarde... Une analyse de quelques millisecondes suffit à vérifier le bon contenu du formulaire.

4-3-1-1. Validation de la base de données dorsale

Lors du processus de validation de l'environnement sélectionné, une procédure va identifier l'existence de la base de données dorsale qui a été définie dans la zone de texte. Le processus ne vérifie pas l'intégrité de la base en nombre de tables, mais seulement l'existence du fichier.

Rien ne vous empêche si le coeur vous en dit de mettre oeuvre un algorithme idoine pour ce faire en comparant l'existence et la structure de toutes les tables de la base de production en regard de celles des bases de test et de recette...

Lorsque cette opération de vérification a abouti, une procédure de mise en place de suppression des tables attachées et de rattache de ces mêmes tables issues de la base de données sélectionnée est exécutée. Toutes les liaisons sont rétablie à l'image de l'opération similaire opérée par le Gestionnaire de tables liées.
Votre application sera alors configurée dans un environnement de Test, d'UAT1 ou de Production selon le cas.

Image non disponible
Message de confirmation de la sauvegarde

Lorsque la sauvegarde est terminée avec succès, un message vous le confirme clairement.
Ensuite, le formulaire se referme (cela est prévu ainsi ici, vous pouvez très bien modifier cette opération).

 Impossibilité de sauvegarder l'environnement

S'il a été omis de renseigner un élément ou bien si un élément est invalide, un message d'erreur s'affiche et vous précise l'élement en question dans l'intitulé du message d'une part et lui donne le Focus d'autre part.

Image non disponible
Message d'échec de la sauvegarde

Vérifiez alors quel élément est susceptible d'être responsable de ce rejet.



* quand j'évoque mon projet, je fais allusion au projet qui m'a donné l'idée de rédiger ce tutoriel.

4-3-2. Exécution des processus de traitement

Quel que soit le contexte, évolution ou correction de l'application, il est évident que de procéder à des tests de non-régression est indispensable. Aussi, vous avez différents choix possibles quant à la mise à disposition des modifications que vous auriez apportées.

Remarque

Différence entre évolution et correction
Il existe une différence fondamentale entre la notion de "correction" et la notion "d'évolution".
Il vous appartient de bien définir le contexte avant d'entreprendre la mise en application de ce tutoriel et ce, afin de ne pas vous emmêler les crayons si je puis dire…
Faire "recetter" votre application du fait qu'il ait eu lors de la dernière livraison des bugs ou des dysfonctionnements n'est pas tout à fait la même approche que faire "recetter" votre application dans un contexte d'évolution suite à de nouvelles demandes.
Même si l'un et l'autre se rejoignent, la philosophie de déploiement de votre application fraîchement "corrigée" ou fraîchement "évoluée" n'est pas vu du même œil par l'utilisateur.
C'est pourquoi une grosse organisation de travail est nécessaire lorsque vos projets prennent une envergure de ce type et que ces derniers nécessitent une validation avant d'être distribués à grande échelle.
Là encore, un remplissage de commentaires idoines au sein de votre code doit être parcimonieusement entrepris.

4-3-3. Scénarii d'importation et d'exportation

Sans rentrer dans le détail technique de la chose, les scénarii d'importation et d'exportation n'ont lieu d'être évoqués ici pour parfaire une situation complète en rapport avec ce tutoriel où l'objectif consiste à importer des données issues de fichiers plats et de générer des rapports (statistiques...) vers Excel par exemple.

4-3-3-1. Scénario d'importation

J'entends par "Scénario d'importation" le phénomène qui va vous permettre de remplir la table cible en faisant en sorte que les fichiers soient bien importés depuis le dossier que vous avez spécifié ; le programme chargé d'avaler le fichier pourra extraire le contenu après avoir vérifié qu'il n'a pas déjà été importé (par vérification du nom (horodatage) du fichier identique enregistré dans la table des fichiers déjà traités qui est censée exister dans l'application).
Une fois l'importation effectuée et le traitement de remplissage de la table terminés avec succès, le fichier source est alors déplacé vers le dossier d'archive.

4-3-3-2. Scénario d'exportation

J'entends par "Scénario d'exportation" le phénomène qui va vous permettre de générer les fichiers de sortie en faisant en sorte que les fichiers soient bien déposés dans le dossier que vous avez spécifié ; ils pourront aussi si cela est prévu, être copiés dans la base documentaire du site SharePoint quelle que soit l'adresse (celle-ci pouvant varier selon l'environnement)...

Le programme chargé de générer les fichiers pourra être exécuté autant de fois que l'utilisateur le souhaite (avec ou sans écrasement3 - c'est mieux avec).
Une fois l'exportation des données (filtrées ou non par l'utilisateur) effectuée et terminée avec succès, le fichier source peut alors être déplacé vers le dossier des fichiers de sortie....

Attention ! Le code affairant aux processus d'importation et d'exportation ne fait pas partie de ce tutoriel qui reste un exemple de suggestion pour un contexte professionnel donné.

4-3-4. Comportement du code

 Lorsque que le projet est en production, il ne doit pas exister de différence de comportement entre votre version de développement et la version effectivement mise en production ;
Ainsi, la version de Test, d'UAT1 ou de Prod doivent de comporter à l'identique.
En revanche, si par exemple une nouvelle fonctionnalité a été ajoutée dans un formulaire, elle doit fonctionner que si l'environnement le permet. En d'autres termes, un bloc de code conditionnel pourra par exemple être ajouté pour pouvoir faire en sorte que la nouvelle fonctionnalité soit exploitable dans un premier temps uniquement en mode Test pour vous et une fois fonctionnelle, en mode Recette par la suite.
On considère ici que les 3 environnements Test, UAT et Prod sont affectés à des identifiants numériques statiques (ID) et qu'en aucun cas, leur valeur ne peut changer.
On pourra alors supposer les valeurs suivantes :

  • Test: IDEnvironment=1
  • UAT : IDEnvironment=2
  • Prod : IDEnvironment=3
4-3-4-1. Côté déclaration des différents environnements

Ainsi que cela a été exposé, nous pouvons alors mettre en place une énumération pour définir ces différents environnements.

Plusieurs solutions s'ouvrent à vous pour les gérer :
Je vous propose ici de déclarer une variable qui instanciera la classe spécifiquement dédiée à la gestion de l'environnement soit par exemple :

Déclaration de la variable de classe et de l'énumération
Sélectionnez
Public g_cEnvironment As New clsEnvironment

Public Enum g_eEnvironment
	EnvironmentTest = 1
	EnvironmentUAT = 2
	EnvironmentProd = 3
End Enum

Si vous êtes dans un cas d'ajout d'une nouvelle fonctionnalité dans un formulaire existant (par exemple un bouton de commande qui permet d'exporter vers Excel le contenu de ce qui est consulté à l'instant T dans ce même formulaire), vous devrez alors modifier les lignes dans le code de l'événement Form_Load() par exemple ; le nouveau bloc en environnement de Test ressemblerait alors à ceci :

Exemple d'événement Form_Load
Sélectionnez
Private Sub Form_Load()
	'Bloc de code existant en amont
	'[...]
	With g_cEnvironment
	    Select Case .GetCurrentEnvironment()
	        Case EnvironmentTest
	            cmdExportToExcel.Visible = True
	            'cmdExportToExcel.Enabled = True
	        Case EnvironmentUAT
	            cmdExportToExcel.Visible = False
	            'cmdExportToExcel.Enabled = False
	        Case EnvironmentProd
	            cmdExportToExcel.Visible = False
	            'cmdExportToExcel.Enabled = False
	End With
	'Bloc de code existant en aval
	'[...]
End Sub

Un autre exemple pourrait ressembler à cela :

Exemple d'événement Form_Load
Sélectionnez
Private Sub Form_Load()
	'Bloc de code existant en amont
	'[...]
	With g_cEnvironment
	    cmdExportToExcel.Visible = (.GetCurrentEnvironment() = EnvironmentTest)
	    'cmdExportToExcel.Enabled = (.GetCurrentEnvironment() = EnvironmentTest)
	End With
	'Bloc de code existant en aval
	'[...]
End Sub

avec le même choix à définir entre Enabled et Visible.

Remarquez que j'ai mis en remarque la propriété Enabled ; a vous de choisir entre Visible et donc de préférer un bouton Invisible ou un bouton Désactivé. Une fois le code de ce bouton opérationnel, la condition Case EnvironmentUAT verra les valeurs des propriétés affectées à True.
Là, c'est à vous de choisir entre appliquer un état visible ou désactivé selon vos préférences ou bien un Exit Sub ou un Exit Function en début de procédure événementielle déclenchée par l'événement Click de ce fameux bouton cmdExportToExcel.



Remarque
Tout ce que je viens de vous évoquer ici est circonstanciel à des modifications multiples qu'elles soient mineures ou majeures.
Vous avez bien entendu différents procédés qui vous permettront de mettre à disposition une ou plusieurs nouvelles fonctionnalités.

Ceci reste avant tout une suggestion. Il existe en effet moult possibilités de faire évoluer votre projet...


Duplication des objets

Pour faire évoluer de façon majeure les fonctionnalités d'un formulaire, vous pouvez avoir recours par exemple à la duplication de ce formulaire pour y apporter la ou les nouvelles fonctionnalités. Ainsi, vous conservez le formulaire original sans modifications, ce qui vous ouvre alors la liberté de procéder à modifications majeure dans la version dupliquée sans altérer l'original...

Attention : Ce choix est plutôt à envisager en cas d'évolution et non de correction de bugs.

Ce mode opératoire facilite quelque peu la mise en oeuvre car seule la modification du script d'ouverture dudit formulaire est alors à élaborer...

Voici un exemple :

Exemple de procédure d'ouverture d'un formulaire de données selon l'environnement
Sélectionnez
Private Sub OpenMainDataForm()
Dim strWhereCondition                                       As String
Dim strDataFormName                                         As String
Dim strTargetValue                                          As String
	'Bloc de code existant en amont
	'[...]
	strWhereCondition = "[TargetField]='" & strTargetValue & "'"
	'[...]
	With g_cEnvironment
	    Select Case .GetCurrentEnvironment()
	        Case EnvironmentTest
	            'On ouvre le formulaire frmMainData de test
	            strDataFormName = "frmMainData_Test"
	        Case EnvironmentUAT
	            'On ouvre le formulaire frmMainData UAT
	            strDataFormName = "frmMainData_UAT"
	        Case EnvironmentProd
	            'On ouvre le formulaire original
	            strDataFormName = "frmMainData"
	    End Select
	    DoCmd.OpenForm strDataFormName, acNormal, , strWhereCondition, acFormEdit, acDialog
	End With
	'Bloc de code existant en aval
	'[...]
End Sub
4-3-4-2. C'est un peu confus tous ces environnements !

En fait, ce qui est délicat à assimiler ici et je vous comprends, c'est la différence qui peut exister entre les deux environnements Test et Recette car il vous paraît sans doute étrange d'avoir des choses qui fonctionnent dans l'un et pas dans l'autre.

Ce qu'il faut comprendre, c'est que dans un cas d'évolutions/corrections comme celui auquel j'ai eu affaire (plus de 140), il peut exister des priorités de réalisation. Ainsi, vous pouvez très bien rendre opérationnelle certaines fonctionnalités au détriment d'autres simplement parce qu'elle sont inachevées et pour autant, d'autres ont besoin d'être opérationnelles.
Considérez ici qu'un utilisateur n'a pas la même vision des choses que vous en matière de test de sa version de Recette et que donc il est susceptible de vous remonter des incompréhensions, des bugs ou des états de non correspondance entre ce qui était demandé et ce qui a été délivré.
Par ailleurs, la disponibilité dudit utilisateur n'étant pas la même que la votre, vous de votre côté vous avancez sur le développement des autres évolutions ou des corrections pendant que l'utilisateur, lui, teste ce que vous avez apporté sur la version déployée.
Autre point encore, certaines corrections ou évolutions qui doivent être recettées peuvent empietter sur d'autres qui en dépendent et donc, bloquer en quelque sorte votre propre avancée sur les suivantes. Cela demande donc un pointage parcimonieux des avancées.
Pour ma part, mes utilisateurs et moi-même partagions un classeur Excel dans lequel figuraient les différents états d'avancée du projet avec la notion de gravité ou de priorité. Au fur et à mesure que les mises en recette étaient délivrées, de mon coté, je pointais la colonne de l'état de realisation côté développement tandis que les utilisateurs pointaient la colonne qui validait ou non la bonne marche de la fonctionnalité ou la correction effective du bug pour une ligne donnée.
Cette façon de procéder a permis un véritable suivi pour mener à bien la finalisation de l'application.

4-3-4-3. Et ensuite ?

Eh bien une fois que la validation de la version de Recette est effective, il vous suffit de copier/déplacer (selon l'architecture que vous ave mis en place, tous les blocs de code s'exécutant dans cet environnement vers celui de Prodution.

Vous condidérerez alors que cette action engendre la notion de version en vigueur.

Cette opération est identique entre la version de Test et la version Recette...

Bien entendu, il est recommandé de commenter ces opérations avec une date pour un meilleur suivi.

4-3-5. Le mode réservé au développeur

Vous avez effectivement remarqué la présence du champ ReservedToDeveloper dans la table des environnements. Dans mon projet* évoqué au début de ce tutoriel, j'avais ajouté ce champ pour me créer un environnement personnel non accessible aux utilisateurs.

Le but et le rôle de ce champ étaient de pouvoir effectuer certaines manipulations sur mon poste de développement et faire en sorte que l'application s'adapte en conséquence pour l'environnement de Test. Parmi ces fonctionnalités particulières, une m'était propre et inévitable :

Les chemins définis pour cet environnement étaient en fait ceux pointant sur des lecteurs logiques de mon ordinateur portable en lieu et place des chemins UNC4 déclarés pour l'environnment de Test de mon poste local dans l'entreprise. J'avais donc créé pour la circonstance deux lecteurs H:\ et R:\ et ainsi faire ensorte que mon portable et ma station fixe locale de l'entreprise possèdent les mêmes lecteurs mappés avec une structure des dossiers identique et évidemment, tout le contenu.

Lorsque je travaillais à domicile sur ce projet, je changeais manuellement l'environnement et basculait le mode Test où le champ ReservedToDeveloper était à True. Cela me permettait donc de travailler dans une situation identique quelle que soit ma position géographique.

* quand j'évoque mon projet, je fais allusion au projet qui m'a donné l'idée de rédiger ce tutoriel.

5. Conclusion

J'ai souhaité partager avec vous ce nouveau tutoriel car j'estime, de par mes années d'expérience, qu'il est important de pouvoir développer vos applications avec la plus grande souplesse possible.
L'emploi de fonctions factorisées, l'usage de procédures génériques et la bonne écriture du code sont déjà un piédestal.
Cette nouvelle brique que vous venez de lire et pour laquelle je vous laisse imaginer la souplesse de l'architecture avec laquelle vous pourrez dès lors avec une grande aisance faire évoluer vos projets. Ce sera un plus pour vos clients ou vos utilisateurs et bien entendu, une valeur ajoutée pour vous-même.

6. Sujets corrélatifs

Dans le fil complémentaire de cet article, il est conseillé de lire les tutoriels sur :

- La mise en place de bases de données Access en réseau
- Déploiement d'applications Access 2010 (et antérieures)

7. Remerciements

Je tiens à remercier toutes celles et ceux qui ont participé à la relecture de ce document en y incluant leurs remarques et en particulier :

Dolphy35
Philippe JOCHMANS
Pierre Fauconnier
et GAYOT

sans oublier Nono40 pour son outil permettant la rédaction des articles.

8. Glossaire

1 - UAT

User Acceptance Testing
Equivaut au terme français "Recette" et qui est la dernière phase d'acceptation avant la mise en production...

2 - Surcharge

La surcharge de méthode consiste à créer de nouvelles méthodes portant le même nom mais ayant des paramètres différents (sans évoquer le nom) de la méthode initiale.
Cela permet de conserver une seule et même nom de fonction et pour lesquelles des détails changent.
Voir l'article de Mohamed Elhadi Benzeghiba (https://mbenzeghiba.developpez.com/tutoriels/dotnet/heritagevbnet/)

3 - Ecrasement

Dans le contexte, on suppose que tout fichier généré n fois pour une même période efface et remplace les fichiers existants et portant un nom identique qui ont déjà été généré auparavant.
  - Sous Excel en OLE Automation, vous définirez la propriété DisplayAlerts à False pour l'objet "Application" instancié.
  - En usant de FileSystemObject, le méthode CreateTextFile verra son paramètre Overwrite défini à True.

4 - UNC

En informatique, Universal Naming Convention abrégé UNC est une convention sur une manière de définir l'adresse d'une ressource sur un réseau, mise en oeuvre par Microsoft Windows®.
Plutôt que de spécifier une lettre de lecteur et un chemin d'accès (par exemple, H:\partage), un nom UNC utilise la syntaxe suivante :
\\serveur\partage\chemin\nom_fichier

5 - Document Libraries

(Voir Working with sharepoint document-libraries)

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Ce document est issu de https://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.