Proposée par Linuxos une création Web à partir du composant gb.web.gui de Gambas3.
Première partie :
Suivent quelques captures d'écran de son dernier projet.
Ce projet est né d'un besoin de pouvoir concentrer toutes les informations liées à des appartements et éviter d'en avoir partout, en désordre.
Après maintes recherches de ce qui existait déjà dans le domaine open source, soit je n'ai pas trouvé quelque chose de tout simple, soit quelque chose de trop compliqué, soit c'est payant, donc pas le choix...
Je dois aussi avouer que je manquais de projet motivant en Gambas, ces temps-ci donc, c'est parfait.
J'aurais pu développer toute l'application en mode Bureau (Interface graphique avec GTK ou QT) mais j'ai préféré tout faire en application Web avec le composant 'gb.web.gui' que je trouve fantastique.
Bien que ce composant ne soit pas terminé, il est déjà très fonctionnel et même si je perds des facilités comparé aux objets graphiques disponibles avec QT ou GTK en Gambas, cela me force a trouver d'autres façons de faire et pour le moment je n'ai pas eu de blocage à coucher mes idées sur le projet en lignes de code.
Cela me permet, aussi, de me former plus en profondeur avec ce composant et par là même de découvrir des Bugs (ou des améliorations a apporter) et les remonter à Benoît pour faire grandir ce composant.
Je reconnais que cela demande de penser différemment avec ce type d'application, dite WEB, car la communication entre son application en Gambas et le Navigateur Web de l'utilisateur, met en pratique des fonctionnements différents d'une application Bureau (ou Desktop).
Cette application Web sera aussi un bon exemple pour d'autres et peut-être une aide s'ils décident de se lancer dans ce domaine de Gambas.
Voici les images pour vous donner un avant goût:
Accès au site Web :
Page d'Accueil :
Page d'entrée :
Le logiciel lui-même :
Deuxième partie :
Le projet d'Application Web 'gbGestion Immobilier' suit son cours, avec son lot de défis, évidement.
L'application n'est pas terminée et il manque encore plusieurs parties mais celles, déjà codées sont presque totalement fonctionnelles.
Afin de pouvoir aider d'autres personnes à se lancer dans la réalisation d'Application Web (avec le composant gb.web.gui), je vous fournirai plusieurs schémas, documents, décortiquant au mieux l'intérieur de mon projet, avec une approche didactique si j'arrive à le faire.
Dans la suite de cet article, j'emploierai la notion "d'Application Web" et non de 'Site Web' (même si au final cela en est un), car dans Gambas, c'est vraiment comme développer une Application et non pas une suite de Page Web en HTML (même si c'est possible aussi en Gambas).
À part quelque fois, vous ne ferez principalement que du code Gambas (peut-être un tout petit peu de CSS).
Afin de mieux comprendre comment fonctionne une Application Web dans Gambas, voici un petit diagramme explicatif:
Souvent il vaut mieux un bon diagramme qu'un long discours.

La différence avec un Simple Site Web, c'est que chaque Page envoyée au Navigateur Web, intègre un petit bout de Javascript qui surveille les actions de l'utilisateur.
Ainsi lorsque, par exemple, l'utilisateur clique sur un bouton de la Page Web, le Navigateur Web (au travers du Javascript) en informe votre Application Web, qui agit comme si c’était un Évènement normal de Gambas, et renvoie au Navigateur Web uniquement le petit changement de la Page Web a modifier (en non pas l’entièreté de la Page Web modifiée), ce qui fournit le même ressenti Utilisateur qu'une application Bureau sur votre ordinateur (Terme anglais employé: Application Responsive)
Pour commencer voici, le schéma de la structure de la base de données :
et le détail de l'organisation des fichiers/répertoires du projet :
De plus je joins 3 petites vidéos pour vous montrer comment ajouter, supprimer des Documents ; des Photos, ainsi que le téléchargement des dits documents.
Vidéos :Vidéo Ajout documentVidéo Ajout PhotoVidéo Téléchargement Document
Maintenant parlons un peu des défis rencontrés:Défi 1 - Comme toute Application Web, la question de Login/Password pour se connecter est importante pour des raisons de sécurité d’accès a des données sensibles ou pas (mais pas indispensable sinon).
De plus cela sert surtout à identifier l'utilisateur qui se connecte afin de récupérer son profil, ses préférences, ainsi que déterminer à quelles parties/fonctions de l'Application, il aura accès.
- Généralement, une table est là dans la Base de Donnée pour cela, contenant tous les Login/Password (format encrypté), ainsi que les accès auxquels ils ont droit :
- Ex: Est-il 'Admin' ou pas
- A-t'il un accès en lecture seule ou en lecture/écriture
- Quelle partie de l'Application, auquel il pourra accéder
- etc...
Défi 2 - La définition de la structure de la Base de données est important et doit plutôt être effectué (dans les grandes lignes) dès le départ, cela permet de mieux structurer son Application et d'avoir une vue d'ensemble dès le début. Si l'Application n’intègre pas de base de données, rien n'est à faire.
Défi 3 - Si besoin, comment 'envoyer un document/fichier à son Application Web au travers du Navigateur qui affiche la dite Application. Pour cela Gambas fourni l'objet 'WebFileButton' qui fait le travail pour vous (enfin la partie la plus difficile) et il ne vous restera qu'à gérer le transfert dans Gambas.
Je reconnais que cela n'a pas été facile pour moi de comprendre comment cela fonctionne et comment le gérer en code Gambas.
Je vous invite à fouiller dans mon code pour avoir un exemple (quand j'aurai mis à disposition le projet...sous peu).
Défi 4 - Une fois que l'on a codé la partie qui permet d'envoyer un document à l'Application, il faut se pencher sur le 'comment faire pour télécharger' un document depuis l'Application Web à l'utilisateur. Dans le cas de mon Application, chaque bien Immobilier peut avoir plusieurs documents, de tous types, qui lui sont attachés, comme des documents d'Assurance, de Bail, etc..., et il faut bien que l'utilisateur puisse les consulter, au besoin.
Ici le défi est un peu plus compliqué car selon le type de document, il faut agir différent.
Par exemple, pour un fichier PDF, l'utilisateur généralement clique sur le document dans la Page Web et c'est le Navigateur qui reconnaît que c'est un fichier PDF et donc il se charge de vous le présenter directement à l'écran, aucun autre programme n'est nécessaire de nos jours, les Navigateur Web savent faire ça. Il en va de même avec les Images, les Vidéos.
Mais dans le cas d'un fichier ZIP, généralement le Navigateur se contente de le télécharger puis de le sauvegarder sur votre disque.
On comprend rapidement ici, que plus on laisse au Navigateur la tache de Visualiser/Sauvegarder/Gérer le type de document, mieux c'est, et moins de code on a développé dans son Application Web.
Il serait possible, bien sur, d'un autre coté, de gérer la Visualisation des dits documents directement dans l'Application Web, mais cela veut dire 'plein de code' comme: "Interpréter un fichier PDF et le visualiser a l'écran", ou un fichier Texte, etc...
Donc, si nous décidons (comme moi), d’être un peu fainéant et de laisser faire le travail au Navigateur de Visualiser le document, il est nécessaire que le Navigateur puisse le télécharger depuis l'Application Web. Hors, cela est facile quand une Application Bureau peut accéder à votre disque dur, mais dans le cas d'une Application Web, dont le but est d’être accessible sur le réseau (donc à distance), et bien là, on comprends que le Navigateur ne peut plus accéder au disque dur distant où se trouve l'Application Web qui stocke le Document directement (et , heureusement).
De plus, il existe des restrictions au niveau des Pages Web (ou HTML) et il n'est pas possible pour une page Web qui s'affiche sur le Navigateur, d’initier le téléchargement du Document pour l'utilisateur, il faut absolument que ce soit une action décidée par l'Utilisateur afin de vouloir télécharger le Document ou un Fichier, en 'Cliquant' délibérément dans la Page Web.
Donc nous en venons à notre défi qui est que votre Application Web ne pourra pas INITIER le téléchargement du dit Document pour l’utilisateur quand celui-ci cliquera sur un bouton de votre Application Web.
Pour cela, il sera nécessaire d'afficher dans la page de l'Application Web, un bout de pur HTML, qui déclenchera le mécanisme de 'téléchargement' depuis le Navigateur vers votre Application Web pour lui demander de vous envoyer le dit Document.
J'ai réussi ce défi, comme montré dans la vidéo, en affichant un panneau qui demande a l'Utilisateur de 'cliquer' sur un Bouton pour lancer le Téléchargement du Document vers le Navigateur.
Comme vous le constaterez dans la vidéo, le lien (ou bouton) qui est présenté à l'Utilisateur n'est valable que quelques secondes et devient inopérant si l'utilisateur ne clique pas dessus. Ceci est dû à un autre défi que je n'ai pas pu régler normalement (du moins je n'ai pas su le faire jusqu’à présent), car la fonctionnalité n'est pas présente dans le composant gb.web.gui (J’espère ne pas dire de bêtise là ??)
En effet, tout document, fichier, image, etc... qui font partie d'un page Web (ici celle renvoyée par l'Application Web), est envoyé par le Mini Serveur intégré dans l'Application Web. Hors celui n'a accès qu'au répertoire '.public' du projet Gambas, que ce soit lors de l’exécution par IDE, ou à l’intérieur de l’exécutable *.gambas.
Partant de ce constat, tous les documents, photos, envoyés à mon Application Web devront obligatoirement atterrir dans le répertoire '.public/' afin que le Mini Serveur Web puisse par la suite les envoyer au Navigateur.
Ici donc se pose un véritable problème car de ce fait, tout fichier, présent dans le répertoire '.public' sera accessible par une simple requête Web depuis le Navigateur de l'utilisateur, ainsi que les fichiers importants, qui ne sont accessibles normalement, que si l'utilisateur est connecté avec un Login/Password.
Généralement ce problème n'arrive pas avec les Sites Web dans d'autre langage, car lors de la première connexion au Site, avec Login/Password, le Site Web génère un 'Jeton' (longue suite de valeurs Hexadécimales) qui va suivre l'utilisateur durant toute sa session, et ainsi, toute demande de fichier, image, etc... au Site Web, devra inclure ce dit 'Jeton' dans la requête, pour prouver que l'utilisateur est bien autorisé. Ce 'Jeton' expirera si l'utilisateur se déconnecte (Logout) ou s'il n'y a pas eu d'activité de la part de l'Utilisateur durant le temps autorisé de la session.
Dans le cas de Gambas (ou plutôt gb.web.gui), cela ne fonctionne pas car le Mini Serveur Web de l'Application Web est comme qui dirait autonome, il n'a de compte a rendre à personne quand il reçoit une requête depuis le Navigateur, donc en aucun cas il ne se préoccupe de vérifier si un quelconque 'Jeton' est valide ou pas.
Tout ceci pour vous expliquer, pourquoi dans la vidéo je créé un 'lien symbolique temporaire' dans le répertoire '.public/lien_document/' vers le vrai lieu où se trouve le fichier à télécharger par l'Utilisateur. De cette façon, seul l'Utilisateur, pendant un cours instant, pourra télécharger le fichier, grâce au 'Lien symbolique', et juste après quelques instants, le 'Lien symbolique" disparaîtra. L'utilisateur aura eu le temps de télécharger le Document, mais après quelques secondes, plus personne ne pourra le télécharger, le 'Lien symbolique' aura disparu. Je suis conscient que ce n'est sûrement pas la meilleur façon de procéder, mais pour le moment je n'ai pas trouvé mieux, ou je n'ai pas su faire.
Je m’arrête ici ma très longue explication, en espérant que ce sera un peu compréhensible.
N’hésitez a revenir vers moi si certain passages sont Lunaire...

Suite des défis.
Défi 5 - BD, AUTO-INCREMENT, INDEX
Lors de la définition des tables de la base de données, il y a quelques astuces à ne pas manquer qui facilite grandement le gestion de la base de données par la suite.
L'IDE de Gambas intègre nativement un Éditeur de base de données, fort bien pratique, mais qui ne gère pas toutes les spécificités des différentes base de données, tels que SQLite3, MySQL ou PostGreSQL, etc...
Hors, comme vous le constaterez dans chaque tables que j'ai défini, il y a toujours un champ 'ID' (Ex: id_bien_document, id_document), et celui-ci est important car il doit-être Unique et généralement de type Nombre Entier. De ce fait, durant la vie des dites tables, les enregistrements sont amenées à être Ajoutés, Supprimés, donc techniquement il va falloir s'assurer de ne pas avoir de doublon d'ID sinon SQLite3 va retourner une erreur de violation de contrainte. c'est ici que la première astuce intervient pour faciliter tout cela mais nécessite d'utiliser un programme externe a l'IDE car non disponible dans l’Éditeur de BD de Gambas.
Pour ma part j'utilise 'DB Browser for SQLite' (mais aussi "DBeaver Community") afin de rajouter l'option 'AUTO-INCREMENT" sur le champ ID de toutes mes tables. Avec cette option, plus besoin de gérer l'ID d'une table dans son code, c'est automatique et magiquement géré par SQLite3 lui même.

La 2eme astuce concerne l'ajout d'Index sur les champs des tables de la BD sur lesquels vont se faire les principales recherches, tris, regroupements. Il va de soit que pour un petit nombre de données dans une table, la différence entre avoir des INDEXes ou pas est ridicule en terme de temps d’exécution de requête, mais pour un grand nombre données à traverser, la différence sera plus que significative. Cela fait partie des choses à faire pour améliorer les performances des base de données.
Défi 6 - Verrouillage de l’arrière plan d'une Application Web lors de PopUp
Dans mon Application Web, il peut y avoir des panneaux 'PopUP' qui apparaissent pour saisir/éditer un Locataire, une Note, etc...
Lors de l’apparition du PopUp dans la Page Web, il faut s'assurer que l'Utilisateur ne puisse pas cliquer partout dans le reste de la Page Web qui se trouve derrière le PopUp.
Au début j'utilisais la technique qui consiste a Verrouiller (Ex: WebButton.Enabled = False) tous les Objets de la Page Web qui étaient en arrière plan du PopUp mais lors du Verrouillage, les Objets verrouillés avaient tendance à disparaître un peu et devenir tout Grisâtre. Cela fonctionne très bien mais n'est pas très joli.
Voici donc une autre façon de procéder qui est beaucoup plus élégante et pas très compliquée à mettre en œuvre. L'idée c'est que lorsque le PopUp est affiché en avant plan de la Page Web, juste avant, on fait afficher un WebForm dont le fond est 'Transparent' qui se retrouvera affiché ENTRE la Page Web et le PopUp. Ainsi, la Page Web ne sera pas altérée visuellement (contrairement au Verrouillage des Objets expliqué précédemment), mais l'Utilisateur ne pourra plus rien 'cliquer' dans la Page Web, excepté ce qui se trouve dans le PopUp. Simple et élégant (Peut-être cette technique est utilisée classiquement mais je ne le savait pas avant).
Suite aux remarques de Gambix concernant les INDEX AUTO-INCREMENT, j'ai fait la modif dans toutes les tables de mon projet pour utiliser le TYPE 'Serie' plutôt que ma précédente solution.
Donc pour illustrer la remarque de Gambix:

Je reviens sur l'option que Gambix mentionnais plus haut à propos du Type 'Série' (ou DB.Serial) et voici mon retour d’expérience.
Il se trouve que j'ai dû faire marche arrière et revenir à ma configuration des ID de mes Tables de base de données pour la raison suivante :
Tout d’abords, lorsque vous manipulez la structure d'une base de données, un conseil, faites une sauvegarde avant, car tout peut arriver.
Dans mon cas, le changement de Type de Champ ID des tables de BD de 'ENTIER' + Clé Primaire + AUTOINCREMENT, s'est soldé pas une perte des données déjà présentes dans chaque table modifiée, au travers de l’Éditeur de BD de Gambas IDE.
Heureusement j'avais fait un sauvegarde par précaution.
Après plusieurs manipulations/essais, il s'avère qu'avec la bonne suite d'action pour changer du Type 'ENTIER' + Clé Primaire + AUTOINCREMENT -> au Type 'SERIE' + Clé primaire, il n'y pas de perte de données, mais c'est quand même très étrange.
Voici la procédure qui a fonctionné, pour moi, sans perte de données :
1 - Afficher la structure de la table concernée dans l’Éditeur de BD de Gambas
2 - Cliquer sur l'onglet 'INDEX' pour afficher les INDEXes configurés
3 - Supprimer l'INDEX relatif au Champ ID de la Table si il y en a un
4 - Sauvegarder les modifications de la Table - /!\ Étape importante
5 - Cliquer sur l'onglet 'CHAMP' pour afficher la liste des Champs de la Table
6 - Sauvegarder les modifications de la Table - /!\ Étape importante
7 - Cliquer sur l'onglet 'INDEX' pour afficher les INDEXes
8 - Créer un nouvel INDEX pour le Champ ID modifié dans les étapes précédentes
9 - Vérifiez que toutes les données dans la Table sont toujours là
Maintenant, voici pourquoi ma solution de 'ENTIER' + Clé Primaire + AUTOINCREMENT me convient mieux. En fait cette solution, plutôt que le type 'SERIE', dans l’Éditeur de BD de Gambas, me laisse plus de souplesse, car dans l’Éditeur si je supprime des enregistrements, je suis tout de même libre de saisir le numéro d'ID que je souhaite pour mes tests alors que dans le cas de d'ID de Type 'SERIE', l’Éditeur de Gambas incrémente indéfiniment l'ID et ne me laisse, en aucun cas, la possibilité de saisir le mien.
Même si je peux forcer un ID de mon choix dans l’Éditeur de BD, lors de l'insertion d'un nouvel enregistrement dans la Table par la commande SQL 'INSERT INTO ...", tout se passe bien et SQLite défini lui même le prochain numéro d'ID à utiliser.
Je pense que ce sont 2 fonctionnements différents, Type 'ENTIER + Clé Primaire + AUTOINCREMENT' ou Type 'SERIE', mais les 2 sont corrects. C'est juste que dans mon cas, le Type 'ENTIER ..." me laisse plus de souplesse pour le développement de l'Application Web et du débogage.
Comme promis, j'ai déposé
ici, dans la forge du Site, le projet.
Toutes les données dans l'application, sont ici, à titre d'exemple et toutes fictives.
J'utilise Gambas version de DEV (3.20.99 fca60d8c8 Master) car Benoît a corrigé quelques bugs liés justement à JIT et à gb.web.gui/Mini Server Web
Il se peut que cela fonctionne très bien avec la dernière version stable de Gambas
J'ai aussi ajouté la variable GB_HTTPD_HOST pour limiter l’accès à une IP, dans mon cas 127.0.0.1, et j'ai poussé ma modif à Benoît mais je ne suis pas sûr quelle aie été déjà ajouté à la branche MASTER de Gambas.
Dans votre cas, cela n'aura pas d'impact, juste que l'Application répondra à toutes requêtes réseaux: 0.0.0.0
Je précise que certaines parties ne sont pas encore finies :
- Panneau du Profil Utilisateur
- Suppression d'un Bien Immo (la suppression des données n'est pas encore codée)
- Panneau de la liste de Locataires
- Gestion des droits/accès en fonction de l'utilisateur connecté
Donc après avoir téléchargé l'Application, il faut activer le Mini serveur Web de l'application dans l'IDE.
Le mieux est d'ajouter les variables d’environnement nécessaires (même si des boutons sont disponible dans l'IDE pour ça)


Il est aussi possible de voir tous les Logs de l'application en temps réels, ceci à but informatif et aussi pour montrer comment je DEBUG ce genre d'application:
- Créer le fichier '/tmp/gbGestionImmobilier.log' -> cmd: touch /tmp/gbGestionImmobilier.log
- Suivre les Logs dans un terminal -> cmd: tail -f /tmp/gbGestionImmobilier.log
Login/Password: admin/demo
Pour finir, la
vidéo du jour pour montrer les derniers ajoutsJ'ai enlevé les documents de test, ainsi que les photos pour alléger l'archive.
Laissez moi savoir si cela fonctionneOlivier
Avancée des travaux :
Voici un petit résumé des avancements du projet.
Vous trouverez les sources du projet dans la Forge du Site. N'hésitez pas revenir vers moi si vous n'arrivez pas a faire fonctionner le projet.
1 - Le projet contient
un Module nommé 'ModVar' dans lequel plusieurs Variables peuvent être ajustés en fonction de votre configuration:
Public PathLogFile As String = "/tmp/gbGestionImmobilier.log" ' Chemin du fichier de Log - Doit être existant pour avoir des Logs
Public PathDatabase As String = Application.Path ' Répertoire ou se trouve le fichier SQlite3
Public DatabaseFileName As String = "database.db" ' Nom du fichier base de données SQlite3
Public FileConfig1 As String = Application.Path &/ "gbGestionImmobilier.ini" ' Non implémenté pour l'instant
Public StorageDocDir As String = Application.Path &/ ".hidden/storage_doc" ' Répertoire physique de stockage des Documents - Valeur par défaut si rien
Public StoragePhotoDir As String = "/local/Gambas-3/gbGestionImmobilier" &/ ".hidden/storage_photo" ' Répertoire physique de stockage de Photos - Valeur par défaut si rien
Public StoragePhotoWeb As String = "/storage_photo" ' Chemin (lien symolique) d'accès aux Photos dans '.public' utilisé par le Mini Serveur Web.
2 - Partie enfin terminée
- Panneau d'Administration
- Uniquement accessible si l'utilisateur est 'Admin'
- Panneau de Profil utilisateur
- Suppression d'un Bien Immobilier
- Suite aux remarques de Gambix concernant la gestions des transactions, dans le cas de Suppression d'un Bien Immobilier, j'ai procédé différemment pour être plus sécurisé.
En effet, du fait que la Suppression d'un Bien Immobilier demande plusieurs actions (suppression de données dans plusieurs Tables de la base), j'ai utilisé la méthode de transaction classique disponible dans SQLite3.
Cette méthode, classique a beaucoup de base de données, permet d'informer la BD qui nous allons effectué plusieurs opérations sur la dite DB, de manière temporaire, et si aucune opération n'échoue, alors on demandera a la BD de commit physiquement tous les changements en un seul coup. Au contraire, si une ou plusieurs opérations échouent (à décider par l'utilisateur), alors toute la transaction est annulée et aucun changement n'est apporté à la BD.
On y voit ici un gain de temps pour appliquer de multiples changements en une fois (beaucoup moins de multiples connections pour chaque modification individuelle), et une Sécurité' car il faut que toutes les actions a appliquer réussissent pour être écrites réellement dans la BD.
Pour ce faire il faut décomposer en 3 phases les actions a réaliser par les mots clés suivants:
A) Avant de commencer des actions sur la base de données on initialise une nouvelle 'transaction'
Si on suppose que '$hConn' est mon objet 'connection a la base SQLite3'
'' Debut de transaction
$hConn.Begin()
'' Transaction
Exemple:
1) Supprimer les enregistrements liés au Bien Immobilier XX dans la Table 'table_document'
==> Si l'action échoue alors on annule tout par: $hConn.Rollback()
2) Supprimer les enregistrements liés au Bien Immobilier XX dans la Table 'table_photo'
==> Si l'action échoue alors on annule tout par: $hConn.Rollback()
3) Supprimer les enregistrements liés au Bien Immobilier XX dans la Table 'table_locataire'
==> Si l'action échoue alors on annule tout par: $hConn.Rollback()
4) Supprimer les enregistrements liés au Bien Immobilier XX dans la Table 'table_frais'
==> Si l'action échoue alors on annule tout par: $hConn.Rollback()
5) Supprimer les enregistrements liés au Bien Immobilier XX dans la Table 'table_note'
==> Si l'action échoue alors on annule tout par: $hConn.Rollback()
et enfin...
6) Supprimer l'enregistrement du Bien Immobilier XX dans la Table 'table_bien_immo'
==> Si l'action échoue alors on annule tout par: $hConn.Rollback()
'' Commiter l'ensemble de la Transaction si tout c'est bien passé avant
$hConn.Commit()
- Ajout d'un Panneau Galerie pour visualiser les Photos
3 - Petite vidéo illustrant les parties finies/améliorées
video_profil_utilisateur_et_suppression_bien.mp4
Voilà pour l'avancement des travaux.
Olivier
====================
Navigation :
<-- Liens du Wiki : <--<-- Accueil du WIKI : <-- ====================
Documentation :
====================