MyPitSelf
MyPitSelf
Bienvenue dans MyPitSelf


English(US) English(US) Français(FR) Français(FR)


Cette documentation a été produite avec MyPitSelf00817

   Bienvenue dans MyPitSelf

MyPitSelf© est un Système permettant de produire des systèmes d'informations, dans un contexte de bases de données. Le mode de conception permet la rétro-analyse des programmes (rétro-ingénierie ou reverse-engineering).

Comme pour tous les Systèmes, on peut toujours penser à une nouvelle fonction à fournir, il n'y a pas de limite.

Or, ce qui fait qu'un Système peut évoluer sans limite est lié au principe de récursivité d'après lequel, par retour sur tout ou partie des éléments de départ, un petit nombre d'éléments finis suffit à construire une série infinie d'éléments.

La rétro-analyse est un processus de type récursif et MyPitSelf est une solution mettant en oeuvre ce principe sur les programmes.

Dans la continuité de cette logique, le premier programme écrit avec MyPitSelf est MyPitSelf lui même.
En effet, c'est aussi un système d'information comme ceux qu'il permet de produire mais, au lieu de gérer des comptes en banque, des mails, des sites web, des processus,,, il gère des programmes et des bases de données.

La mise en oeuvre du principe est réalisée grâce à:
- une méthode permettant de résoudre le problème du paradigme d'accès aux bases de données ( SQL vs language de programmation ).
- Un langage utilisant la méthode d'accès aux données et dont les phrases peuvent être transformées en données et inversement.

Une partie de cette documentation n'est pas trop technique ( voir Présentation Générale ), les autres chapitres le sont.

MyPitSelf utilise MySql, Php, SVG, Apache, HTMLArea, linux, firefox, Win2K/XP/IE6/7 et surtout lui même!

Le nom MyPitSelf vient de MySql, Php et Itself


. . . .


Présentation générale


- Quand c'est compliqué...

J'ai eu un problème quand j'ai du maintenir des systèmes d'information dont les données avaient une représentation qui ressemblait au modèle présenté dans l'image çi dessous.


Cliquez et regardez l'image pendant 5 secondes, pas plus. Utilisez le bouton en haut de la nouvelle fenêtre pour la fermer.

Globalement vous avez vu des boites (les informations) et des flèches qui les relient (les interconnexions entre les informations).

Le problème était que ça devenait trop compliqué car les informations et les interconnexions à gérer étaient si nombreuses que je ne parvenais plus à intégrer toutes les contraintes fonctionnelles ou techniques liées à ce genre de modèle.

Dans ce cas, on appelle des collègues et on fait de gros projets qui prennent du temps à mettre en place et qui finalement ne satisfont pas toujours les utilisateurs.




- ... on trouve des solutions ...

Mais, ils en veulent plus les utilisateurs car ils ont des clients qui menacent en permanence d'aller voir la concurrence si on ne peut pas fournir une nouvelle fonction stratégique généralement pour hier.

Dans ce cas, on fait de la maintenance, et là, ça devient vraiment difficile car, comprendre ce que le concepteur a bien voulu comprendre, reprendre ce que le programmeur a bien voulu programmer et assurer à l'utilisateur que ce sera fait dans x jours pour un coût de y jours/hommes, a toujours été, pour moi, une tâche qui oscillait entre un domaine probabiliste (ça devrait passer) et un domaine quasi mystique (pourvu que ça marche !).

Donc, comme je n'aimais pas cette situation, je me suis mis à développer un outil pour me faciliter la tâche.

Au fur et à mesure de mes développements, je m'apercevais que je me dirigeai tout droit et à grande vitesse dans un mur car ça devenais encore trop compliqué. Il fallait que je trouve un moyen pour pouvoir avancer dans les développements sans prendre le risque de faire croitre le nombre de bugs de façon exponentielle.

J'en suis venu à la conclusion qu'il fallait industrialiser les développements et pour ce faire, il fallait que la conception et l'écriture des programmes puisse être elle même industrialisée.
Et pour ce faire, j'en suis venu à la deuxième conclusion que les programmes devaient pouvoir être analysés et modifiés par leurs propres moyens.

Mais il fallait trouver le moyen de mettre en place toutes ces idées.


- ... on les construit ...

J'ai donc fabriqué MyPitSelf mypitself. Le reverse-engineering étant assuré et le premier programme fabriqué avec MyPitSelf étant MyPitSelf lui même, je pense que les fondations ne sont pas trop mauvaises.

Un jour, j'ai demandé à un professeur de philosophie des sciences et d'histoire des sciences où était la limite de mon système. Il m'a répondu par la citation écrite plus haut à propos des Systèmes récursifs et de leur évolution illimité.
En un mot, seul un environnement de développement ayant comme principale caractéristique la rétro-analyse permet de faire croitre et évoluer des applications sans risquer de tout devoir reconstruire à chaque fois.

Je vous laisse tirer vos propres conclusions sur les outils de développement de systèmes d'informations qui n'offrent pas cette caractéristique.

En fait, la limite du système est imposée par des caractéristiques physiques des ordinateurs tels que la mémoire disponible, le temps d'exécution acceptable, etc... Mais au moins, on peut avancer, les limites fonctionnelles sont beaucoup plus loin maintenant.


- ... et on en parle !

J'ai déjà utilisé MyPitSelf pour faire des programmes dans des situations réelles ( avec des utilisateurs autres que moi-même ) avec des modèles de données qui ressemblent à ça
.
Des applications simples peuvent rapidement être mises en place une fois qu'on est familiarisé avec l'environnement de développement, mais ce n'est pas si évident que ça car c'est différent de ce que je connais de plus classique (vous êtes prévenus).
En revanche, les programmes générés ont une forme classique.

Des exemples simples de développement sont donnés plus bas
Mais on peut aussi faire du compliqué, exemple: MyPitSelf n'est pas un modèle de simplicité fonctionnelle, c'est un environnement de développement prenant en charge le multi utilisateur et les autorisations gérées par groupes d'utilisateurs, le multilangue, le multibase, des environnements multiples, des sauvegardes, la documentation avec des copies d'écrans, etc .....
Le modèle de données que vous avez vu plus haut pendant 5 secondes est celui de MyPitSelf. Ces images de modèles de données sont des copies d'écran de schémas SVG. Grâce à ce SVG, on peut graphiquement spécifier les liens entre les tables.

J'ai commencé à utiliser la rétro analyse pour maintenir MyPitSelf : par exemple, j'avais défini une utilisation pas très propre d'une fonction et je voulais modifier tout le code pour changer ça, comme j'utilisais énormément cette fonction, j'ai écrit une moulinette en langage MyPitSelf qui le faisait à ma place: MyPitSelf maintient MyPitSelf !. Comme cette moulinette a déjà été écrite, si je dois faire une modification sur un autre fonction, ce ne sera pas vraiment compliqué de la modifier.
Certaines personnes appellent ça le "refactoring".

Un autre exemple d'utilisation se produit quand on renomme un champ ou une table de la base de donnée. Dans ce cas, toutes les sources utilisant ce champ ou cette table sont traitées pour remplacer l'ancien nom par le nouveau.

La suite du site est plus technique car j'explique un peu comment tout ça fonctionne . C'est en fait une extraction de la documentation du logiciel. Ces explications font croire que j'ai tout d'abord réfléchi pour faire ensuite: en fait, dans toute ma démarche, l'action a précèdé la réflexion, je n'ai pas honte de l'avouer car je préfère un outil qui marche à un concept qui ne marche pas.

L'important est de pouvoir corriger ce qui ne va pas et ce n'est plus qu'une question de temps.
Cela revient à revendiquer le droit à "l'erreur" à condition que l'on mette en place les moyens de corriger ces "erreurs". Le principe même de l'outil est d'une grande aide !

Il y a encore du travail à faire, certaines parties ne sont pas encore très pures, mais maintenant, je n'ai plus peur de nettoyer, je sais que ça ne deviendra pas un truc inmaintenable et que je pourrais capitaliser.

En fait, en ce moment, je mets en place l'environnement pour travailler avec ajax, vous verrez mes premier essais ici.

Maintenant, au travail !

Le nom MyPitSelf vient de MySql, Php et Itself


- Comment ?


- - Le noyau

Une partie du noyau est écrite directement en php, le reste est écrit en langage MyPitSelf.
Les programmes que vous concevez sont écrits en langage MyPitSelf. Ils utilisent le même modèle que le programme MyPitSelf lui même avec en particulier :

- Une base de données système contenant la description des données et les programmes

- Une ou des bases de données contenant les données gérées ( bdd utilisateur )

L'application cible est conçue dans l'environnement MyPitself puis, le code généré ( php aujourd'hui ) peut être extrait pour avoir une application indépendante de la base de données système et ne travaillant que sur la base de donnée utilisateur.

En d'autre termes, le livrable peut être indépendant de MyPitSelf.


- - Définir les données

Vous commencez par définir un environnement de travail c'est à dire, vos bases de données (champs, tables, index, liens entre les données ...) et d'autres caractéristiques (constantes , droits des utilisateurs, menus ...).

Puis vous générez des requêtes SQL simples grâce à un utilitaire permettant de stocker les caractéristiques de ces requêtes dans le système (type de requête, tables et champs utilisés , ...)

Certaines des requêtes sont destinées à afficher les données (SELECT d'une valeur particulière ou SELECT d'une liste de valeurs).
D'autres requêtes sont transformées en fonctions qu'il est possible d'utiliser dans le langage de MyPitSelf (SELECT, INSERT, UPDATE, DELETE).

C'est là que le paradigme d'accès aux données est résolu: comme l'accès aux données est transformé en fonctions, et que ces fonctions sont intégrées aux programmes, il n'y a plus de requêtes SQL au milieu des programmes source. Seuls les programmes générés contiennent des chaines de caractères contenant les requêtes.

Paradigme: Ensemble des unités qui peuvent commuter dans un contexte donné.




- - Les programmes

Un langage basé sur des appels de fonctions imbriquées est utilisé pour écrire les fonctions au format source. Il utilise les fonctionnalités classiques des langages (variables, tests, boucles, accès fichiers ...) et les fonctions générées par les requêtes.

Aprés analyse syntaxique des sources, le système produit le format table

Le format table peut être mis à son tour en base de données et traité (reverse-engineering). Les fonctions deviennent donc disponibles pour traitement au même titre que les données.

Le système peut regénérer le source a partir du format table.

A partir du format table, le système produit le code exécutable en php à ce jour.


La différence avec un autre language vient du fait que la forme tabulaire du programme qui est la résultat de l'analyse syntaxique est d'une part disponible et d'autre part peut être traitée avec les moyens du language.

Tout se passe comme si vous étiez la personne ayant écrit le compilateur et donc, que vous sachiez comment écrire l'analyseur syntaxique, et qu'à la fin de l'étape d'analyse syntaxique, vous seriez capable de reproduire le source.


- Et alors ?

Et alors, je pense que MyPitSelf résoud ( et résoudra ) quelques problèmes rencontrés en informatique:


- - Complexité des traitements

Problème:
Une application évolue selon deux directions : Les données à gérer et les fonctions à fournir. Aujourd'hui, on peut assez facilement ajouter des données à un système (par exemple, que vous ayez 50 ou 100 emails dans votre boîte de réception ne change pas grand chose) mais ajouter des fonctions est moins évident (par exemple le comptage des mails reçus en provenance de deux ou trois personnes ).

La complexité grandissante des applications demande un traitement industriel. Si la vérification du code ne passe que par des moyens humains, des erreurs se glisseront très probablement dans les programmes.


Réponse:

Une fois les fonctions de base sur les données fournies, la combinaison de ces fonctions pour donner des fonctions plus complexes est une logique simple qu'il est plus facile à appréhender si on considère cette opération comme une opérations sur des données ( ajouter la fonction de recherche de mails en provenance Jean à la fonction de recherche de mails en provenance de Paul ). Le fait de traiter les fonctions comme des données permet de résoudre ce problème.

Dans MyPitSelf, les fonctions sont transformées en données organisées en base de donnée. La vérification de la cohérence de données est une opération courante qui est vérifiable par des programmes Ad Hoc.



- - Adaptation et capitalisation

Problème:

Les données que l'on gère sont définies par un modèle et des règles qui sont justifiées d'une part, au moment de la conception et d'autre part par l'idée que le concepteur se fait de l'application.
Or, cette application doit évoluer dans le temps car au fur et à mesure de son utilisation des fonctions nouvelles sont requises.
Mais aussi, certaines parties qui était importantes lors de la création le deviennent moins et d'autres peuvent prendre plus de poids. Dans ce cas, le modèle de données initial doit pouvoir évoluer de façon à s'adapter à ces changements.

L'important n'est plus modèle de données initial mais sa souplesse et la possibilité de pouvoir capitaliser sur l'existant.


Réponse:

Le premier programme fait avec MyPitSelf est lui même: Il n'y avait au départ que le noyau puis des données ont été ajoutées puis des fonctions sur ces données et ainsi de suite. En d'autre termes, le modèle a du être adapté tout au long de sa conception. Les programmes construits avec MyPitSelf se font sur le même modèle et suivent donc la même logique.

Aujourd'hui les données gérées sont "suffisantes" pour l'environnement de développement dans la version actuelle. Bien que les problèmes rencontrés pour construire ce genre de programmes représentent une bonne partie des traitements que l'on peut imaginer, d'autres données - et fonctions - vont être ajoutées pour gérer des problématiques plus spécifiques, comme par exemple des traitements graphiques. Les applications futures bénéficieront de ces améliorations.



- - Conclusion

MyPitSelf est disponible même s'il n'est qu'à la version 0.8 car le reverse-engineering est aujourd'hui assuré.
Ceci implique que si MyPitSelf évolue, les programmes conçus avec une ancienne version pourront être repris.


- Environnement de travail

95 % des développements sont réalisés via une interface web, seul l'administrateur système doit avoir accès au serveur

Il est multilangue au niveau système, MyPitSelf existe pour l'instant en anglais et en français

Il est multilangue au niveau application, les programmes sont dans les langues de votre choix, l'utilisateur peut choisir sa langue à la volée.

La rédaction de la documentation fonctionnelle est facilitée grâce à la rétro-analyse et à :
  • des copies d'écrans lors des séquences de saisie.
  • la génération de documents HTML basés sur la description de l'environnement.
La gestion des droits des utilisateurs est intégrée dans le noyau et peut être étendue pour s'adapter à vos programmes.

Il facilite la conception d'applications basées sur des modèles de données contenant beaucoup de liens.

Copies d'écrans

Ces copies d'écrans sont extraites de l'exemple de développement expliqué plus bas. MyPitself permet d'enregistrer les pages par lesquelles on passe pour, par exemple, créer la documentation d'un logigiel.

Création d'une requête: .

Création de fonctions: .




Coté technique


- Qu'est MyPitSelf ?

C'est un système pour développer des programmes autour d'une base de données
Chaque développement a son propre environnement qui comprend:

Une base de donnée système qui contient:
- La description des données,
- Les sources des programmes,
- Des valeurs discrètes ( constantes, les répertoires de travail .... )

Une ou plusieurs bases de données contenant les données à maintenir.

Pendant la phase de développement, les environnements sont contenus dans l'environnement principal de MyPitSelf.

Après la phase de développement, on peut produire un ensemble qui est indépendant de l'environnement principal.



Donc, Avec MyPitSelf, en tant que développeur, vous allez construire les environnements pour les autres programmes et remplir les bases de données systèmes de vos programmes.

La cible est de développer des programmes multi utilisateurs donc il y a des gestions d'autorisations basées sur un code/mot de passe de l'utilisateur.


- Pourquoi MyPitSelf ?

Trois raisons principales:

1°) Les SGBD sont des bons systèmes pour stocker et utiliser les données. Ils peuvent aussi être des bons systèmes pour stocker et utiliser les programmes. Donc les programmes devaient être écrits de façon à ce qu'ils puissent être mis en base pour qu'ils puissent être liés aux données qu'ils traitent. C'est pour celà que cet environnement a son propre langage. Ceci veut aussi dire que les programmes sont finalement considérés comme des données, en conséquence, on peut appliquer des traitements aux programmes.


2°) Une fois que les données sont définies, les programmes les traitent. Donc, dans ce système, la première étape est de définir les données et enfin, les programmes peuvent être écrits. Les données de base sont celles définies au niveau dans la base de donnée.

3°) Le premier programme écrit avec ce système est lui même car si ce système peut organiser et traiter des données, il permet aussi d'écrire un système pouvant organiser et traiter les données. Ceci explique la boucle dans ce shéma.



- Comment est-il fait ?

Grâce à PHP, MySql, Javascript et Apache sur window et Linux sur firefox/IE.
Les outils principalement utilisés pour son développement sont lui même (85%), le blocNote de w2k (10%) et PhpMyAdmin (5%) .
Lcc a été utilisé pour écrire un programme permettant de lancer de jobs en batch. C'est une adaptation du programme mikehup.exe écrit par Michael Kohn (naken@naken.cc), Web page: http://www.naken.cc/

J'utilise SVG pour dessiner les tables avec les liens.

PHP est utilisé pour écrire les fonctions de base.

Toutes les fonctions utilisateur devraient être écrites dans le langage de MyPitSelf. Les principes de base de ce langage sont:
  • Les données définissent les actions ( au moyen d'ordres SQL ) et donc une partie du langage. Les requêtes SQL une fois définies permettent de générer des fonctions qui seront utilisées par le langage.
  • Il peut être transformé en tableau de façon à pouvoir tracer facilement l'utilisation de chaque données.
Il est basé sur:
  • Une pile d'appel qui contient les variables déposées par les fonctions.
  • Une pile de variables qui contient des variables prédéfinies et les variables des programmes
  • Des appels de fonctions.


Note sur la traduction: je suis français, j'ai commencé à développer ce logiciel en anglais puis j'ai ajouté la possibilité d'avoir une version localisée en ligne ce qui fait que j'ai pu écrire un peu dans les deux langues, en particulier cette documentation.
Il y a des chapitres que j'ai rédigé directement en français puis traduit en anglais, pour d'autres c'est l'inverse.
Comme le temps me manque un peu, il y a parfois des écarts mais je ne pense pas qu'ils sont très importants, vous saurez vous adapter.

Vous pouvez traduire ce logiciel dans n'importe quelle langue, vous pouvez écrire des logiciels avec MyPitSelf dans votre langue et ensuite les traduire pour d'autres langues.

J'essaierai de maintenir moi même la version française et la version anglaise, si d'autres contributeurs acceptent de traduire ce logiciel dans d'autres langues, je pourrais intégrer leur traductions dans la distribution.
Plus loin dans cette documentation, ( chapitre COMMENT FAIRE ), je parle de la traduction.


- Etat de MyPitSelf

Jamais fini car il y a toujours des nouvelles fonctions à ajouter! Mais suffisament bon pour avoir des sites en production.

Il y a des fonctions comme le versionning, les scénarios de test, la documentation ... que j'ai commencé mais qui ne sont pas encore finies.
Au moins celles qui existent permettent de faire ce que vous allez voir ( si vous continuez ).

Je ne peut pas vous dire quand sortira la prochaine version car je ne peut pas consacrer 100% de mon temps à ce produit.

Maintenant, voyons à quoi tout celà ressemble.



- En pratique


- - Données

Je suppose que vous avez un modèle de données avec une table de gens contenant leur nom et leur age.

Je suppose aussi que vous avez généré plusieurs requêtes grâce au système, dont la requête portant le numéro 1203 (par exemple).

SELECT T0.Id,T0.Nom,T0.Age 
FROM $Data.Gens T0 
WHERE T0.Age <= %SQLPAR0% 
ORDER BY T0.Nom ASC
Une fonction se nommant R_1203_Select() a alors été produite par le système.

C'est le système qui doit générer les requêtes car il commence par stocker toutes les caractéristiques de la requête ( tables impactées, champs utilisés, critères, liens entre les champs, ordre d'affichage, de tri .. ) et à partir de ces caractéristiques, le système génère la phrase SQL et la fonction utilisant la phrase SQL.

Cette requête accepte un paramètre qui est l'age (%SQLPAR0%).


- - Programme source

Une utilisation de cette fonction pourra par exemple servir à afficher tous les gens dont l'age est >= 30.
Cela donnera un programme qui ressemble à (les commentaires sont précédés par //) :

Tac(// Take and call: met des valeurs dans la pile d'appel 
    // et/ou appel des fonctions.
 Cst(30), // constante 30 ans mise dans une pile d'appel
 R_1203_Select(  // Liste de ces gens, le paramètre SQLPAR0 = 30
  Do(
   Take(T0.Nom), // mettre le contenu du champ Nom dans la pile d'appel
   out(1)        // afficher le premier element contenu dans la pile d'appel
  )              // T0.Nom est retire de la pile d'appel a ce niveau
 )
)                // 30 est retire de la pile d'appel a ce niveau
Remarque: Les interlignes sont ignorés, on aurait pu tout aussi bien écrire cette fonction sous la forme:
Tac(Cst(30),R_1203_Select(Do(Take(T0.Nom),out(1))))
De même, les virgules ne sont pas obligatoires après une ). Une autre forme pourrait être:
Tac( Cst(30) R_1203_Select(  Do(Take(T0.Nom),out(1)) ))
Les commentaires de bloc sont délimités par /* et */, ils doivent être en colonne 1 et peuvent être imbriqués.


- - Programme tabulaire

Ce programme aura la forme tabulaire suivante:
Ind Function # Parent # Order In
Parent
Function
Name
Function
Type
Number Of
Children
Level C1 C2
1 1 0 1 Tac f 2 1
2 2 1 1 Cst f 1 2
3 3 2 1 30 v 0 3
4 4 1 2 R_1203_Select f 1 2
5 5 4 1 Do f 2 3
6 6 5 1 Take f 1 4
7 7 6 1 T0.Nom v 0 5
8 8 5 2 out f 1 4
9 9 8 1 1 v 0 5
Sous ce format on pourra analyser et connaître les traitements avec les données impactées car toutes les caractéristiques de la requête 1203 sont présentes dans le système ( type / tables / champs / conditions ).

En d'autre termes, on pourra faire du reverse-engineering.

Si vous mettez ce format tabulaire dans une table de la base de données, vous pouvez aussi modifier ce programme car le système peut regénérer un programme source à partir de la forme tabulaire.

En d'autres termes, il y a une bijection entre l'ensemble source et l'ensemble tabulaire.



- - Source produit

//===============================================================
function f_5001(){
 global $XVS,$XCS; // variable stack and call stack
 $xxSI=sizeof($XCS);  // call stack init pos
 $xxSC=0;  // Stack push count
 //===== Start of function =====
 //===================
 $req4 = "
 SELECT  T0.Nom  FROM  `Gens` T0
 WHERE  T0.Age<=30
 ORDER BY  T0.Nom ASC " ;
 //===================
 $result4=mysql_query($req4);
 if(mysql_errno()==0){
  while($mpsrow4=mysql_fetch_row($result4)){
   echo ''.$mpsrow4[0].'
'; // output } }else{ return(xxErrSql('1203_SELECT',mysql_error(),$req4,$xxSI)); } mysql_free_result($result4); //===== End of function ===== return(true); } //===============================================================
L'exemple précédent est simplifié mais vous pourrez voir en cliquant ici quelque chose d'un peu plus trapu: c'est une des fonction assurant la rétro-analyse de l'exécution d'un programme, elle modifie une forme tabulaire d'une fonction.



- Installation

Je suppose que Apache(1.3.29), Php(4.3.9), MySql(4.0), Firefox ou Ie(6.0) fonctionnent correctement sur votre système. Les numéros de version citées sont celles que j'utilise actuellement mais je suppose que ça marchera avec des versions équivalentes.

Je suppose que vous conaissez un minimum ces logiciels et en particulier le fait de les faire fonctionner ensemble.
Si ce n'est pas le cas, voyez la documentation gigantesque écrite à leur sujet.



- - Dézipper

Il y a deux fichiers à dézipper:
mypitselfw ( fichiers Web ) à dézipper quelquepart sous le dossier htdocs du serveur Web.
mypitselfp ( fichiers programmes ) à dézipper ailleurs que sous le dossier htdocs.

Sur un système linux, vous devez exécuter ces commandes:

cd /var/www # supposons que vous avez mis les dossiers mypitselfw and mypitselfp dans /var/www
chmod -R o+w mypitselfp
chmod -R o+w mypitselfw
cd mypitselfp
chown root:root batch_linux.exe
chmod -R o+x batch_linux.exe


- - Configurer Apache et php


- - - Configuration de php

L'utilisation du fichier php.ini-recommended donné dans votre distribution de Php est fortement recommandée.
Si vous utilisez quelquechose d'autre, à votre guise, mais au moins, vérifiez que les configurations ci dessous sont:
register_globals = Off

J'utilise les paramètre

display_errors = on
display_startup_errors = On
error_reporting = E_ALL

Remarque: ces paramètres ne sont pas recommandés pour les sites en production mais c'est plus pratique pour de déboggage.


- - - Configuration d'Apache

Je suppose qu'apache/php tourne correctement sur votre système.
Ajoutez ce paramètre dans le fichier de configuration d'apache pour aller sur index.php
<IfModule mod_dir.c>
 DirectoryIndex index.php
</IfModule>



- - Installation de MyPitSelf

Quand vous avez essayé de vous connecter au premier écran, vous avez vu un écran comme celui ci.

Si ça n'a pas été le cas, eh bien, quelquechose s'est mal passé, essayez de réinstaller après avoir effacé les dossiers et la base de donnée.
MyPitSelf installation.
Enter here the connection string to mysql
Enter here a valid user name to access to mysql
Enter here a valid password to access to mysql
Enter the full name ( path included ) of the file
MyPitSelf root web path.txt
( It should be already filled )
Use the button to point on the file
MyPitSelf root program path.txt
( It is in the MyPitSelfP directory )
Database initialisation
Create database Delete database

Si vous avez entré des informations correctes dans les champs et que vous avez cliqué sur le bouton Créer la base de donnée vous auriez du obtenir cet écran après un moment.

MyPitSelf installation.
Enter here the connection string to mysql
Enter here a valid user name to access to mysql
Enter here a valid password to access to mysql
Enter the full name ( path included ) of the file
MyPitSelf root web path.txt
( It should be already filled )
Use the button to point on the file
MyPitSelf root program path.txt
( It is in the MyPitSelfP directory )

MyPitSelfxxyzz A été créée avec succès. Vous pouver retourner sur le menu.
ou bien Supprimer la base MyPitSelf

Si vous l'avez effectivement obtenu, c'est bon et après avoir rafraichi la fenetre, vous devriez avoir obtenu une fenetre qui vous demande votre nom d'utilisateur et votre mot de passe. Si ça n'a pas été le cas, eh bien, quelquechose s'est mal passé, essayeez de réinstaller après avoir effacé les dossiers et la base de donnée.


- - Premier accès à MyPitSelf

Cliquez là Vers MyPitSelf
Vous obtenez l'écran qui vous demande votre nom d'utilisateur et votre mot de passe

Entrez "1" dans chaque champ et cliquez sur "Entrer". Maintenant si vous obtenez une fenêtre qui ressemble à celle ci, vous pouvez être content!.




MyPitSelf


Sur cet écran, il y a plusieurs boutons dans la barre de menu.
Cliquez dessus si vous voulez mais, pour le moment, ne cliquez pas sur ceux qui ne sont pas sur les menus svp.

Voyons trois d'entre eux:

Le bouton : Qui vous permet d'aller à l'écran initial.

Le bouton : Qui vous permet d'aller à l'écran précédent.

Le bouton : Qui vous permet d'aller voir la doc.

Le bouton : Qui vous permet de quitter.
Essayez le et réentrez avec 1/1 comme nom d'utilisateur/mot de passe.

Vous verrez dans les exemples permettant de savoit utiliser MyPitSelf des copies d'écrans avec des boutons qui ont une signification standardisée:
: Nouvel élément
: dupliquer un élément
: voir un élément
: Modifier un élément
: Supprimer un élément
: Liste des propriétés d'un élément
: Déplacer un élément




- Documentation générée

A partir des informations entrées pour créer votre application, MyPitSelf peut générer une documentation.
Cette documentation technique contient entre autres le schéma SVG des relations entre les tables.
Un plug-in SVG est disponible sur le site d'Adobe.

Pour y accéder, cliquez ici


Exemples


- Premier exemple: world

C'est une application permettant d'afficher les données provenant de la base de donnée "world" disponible sur le site de MySql.

Le shéma de la base est le suivant


Pour y accéder, cliquez ici




- Deuxième exemple: CRM

Cette application est un petit CRM permettant à un fournisseur de service informatiques de mettre à la disposition de ses clients de type entreprise une organisation leur permettant de gérer leur propres conntacts.

Chaque société aura un utilisateur principal appelé manager.
Le manager crée les contacts ( nom, prénom, .... )
Le manager définit les informations qui pourront être rattachées aux contacts selon ses besoins propres à la société.

Les informations sont de deux types:
- Evènement: c'est une réunion, un déjeuner ... à une date donnée.
- Relation: c'est un fait concernant le contact comme par exemple, il est important, moyen ou petit, il parle anglais, italien ou français, demande des conditions particulières de livraison ...

La liste des contacts est unique pour une société donnée et sera partagée par les utilisateurs de la société en relation avec les contacts.

Le manager définit les utilisateurs ( login, nom, prénom, mot de passe .... ).

L'utilisateur aura une liste de contacts qu'il devra piocher parmi la liste des contacts de la société.

Sur chacun des contacts qu'il se sera approprié il pourra ajouter des relations ou des évènements dans le cadre définit par le manager.

Le contact peut être partagé par plusieurs utilisateurs.

Un système de requêttage permet à l'utilisateur de lister ses contacts satisfaisant à des relations particulières. Ces requêtes peuvent être combinées par des fonction ET, OU, NOT ou XOR.

Le manager peut effectuer ces requêtes sur tous les contacts de la société.

Le modèle de données de cet exemple est celui que vous obtenez en cliquant sur l'image çi-dessous


Vous pouvez voir la documentation générale ( normalement pour chaque type d'utilisateur, il y a une documentation spécifique ) en cliquant ici .

Si vous voulez l'essayer en ligne, envoyez moi un mail par la page contact de ce site, et je vous enverrai une login/mot de passe. Cette application est accessible ici .



Ce n'est qu'un petit CRM mais vous avez vu d'autres possibilités de MyPitSelf.

Je n'ai pas voulu saturer l'application de boutons à cliquer mais il est relativement facile d'en ajouter pour obtenir par exemple à parir de la fiche contact la liste des personnes qui sont en relation avec elles.

On peut aussi ajouter des tables de sociétés, ajouter des liens entre les sociétés et les contacts, et pleins d'autres choses qui demandent un peu plus de programmation.

A bientôt.



- Troisième exemple

Nous allons utiliser cet exemple pour voir l'utilisation de ce système.
Le but est de construire un programme à la demande d'un client.
Les explications sont minimales, ne vous en faites pas si vous ne saisissez pas tout, ça viendra au fur et à mesure.

L'approche projet a trois niveaux.

Niveau organisationnel ( que veut le client ? ).
Il besoin d'un système permettant de gérer les tâches à réaliser. Chaque utilisateur pourra créer ses propres tâches composées d'un nom et d'un niveau de priorité. Le niveau de priorité sera un nombre et chaque utilisateur pourra voir sa liste de tâches triées selon son son ordre de priorité.
Chaque utilisateur pourra trouver les tâches contenant une chaine de caractères.
Les managers pourront aussi voir les tâches des autres utilisateurs mais les utilisateurs normaux ne pourront voir que leurs propres tâches.
Un utilisateur peut modifier, dupliquer ou supprimer ses tâches.

Niveau fonctionnel ( Que devons nous faire ? ).
Une table contenant un identifiant utilisateur, un nom de tâche et un niveau de priorité.
Une table des utilisateurs contenant l'identifiant utilisateur, son nom et son prénom.
Une gestion des groupes ( utilisateurs normaux et managers ).

Nous aurons besoin d'une fonction affichant une liste de tâches selon son niveau de priorité et selon le fait que les tâches contiennent certains mots. A partir de cette liste, on pourra créer des nouvelles tâches, modifier celles existantes ou les supprimer.
Une autre fonction ( manager ) affichera une liste de tache le nom de l'utilisateur.
Donc selon le profil utilisateur, il faudra afficher différents menus:
Utilisateur: Voir mes tâches Manager: Voir mes tâches, Voir toutes les tâches

Un super utilisateur aura la possibilité d'autoriser de nouveaux utilisateurs et de leur assigner un niveau d'utilisation ( manager ou utilisateur )
Un manager sera considéré comme un utilisateur pour ses propres tâches.

Niveau technique: ( Comment allons nous réaliser ceci ).
Voir les chapitres suivants.

Remarque: Comme pendant une modification, il y a un vérou qui est mis en place et que pendant les procédures suivantes il se peut que vous fermiez des fenêtres alors que vous faites une modification, le vérou restera.
Pour le retirer, allez dans le menu Profil puis choisisez l'option Voir les vérous puis cliquez sur le bouton Unlock.


- - Un nouvel environnement

Donc,cliquez ici pour ouvrir une fenêtre si ce n'est déjà fait. sur MyPitSelf et connectez vous en tant qu'utilisateur/mot de passe = 1/1

La première des choses à faire est de créer un nouvel environnement.

Note: pour le moment, je n'ai pas assez travaillé sur les privilèges sur les bases de données donc il n'y a pas de réglage fin de ce coté.
Ca fonctionne en définissant pour chaque environnement un utilisateur/mot de passe ( ou en définissant toujours le même ) et les autorisations sur les bases sont réglées pour cet utilisateur.
Je vous conseil d'utiliser MyPitSelfDev, ça marche !

Pour réaliser celà, vous devrez faire les actions décrites sur les écrans que vous verez en cliquant ici .

Créer un nouvel environnement revient à créer des répertoires et une base de donnée système. ( regardez dans les répertoires mypitselfp et mypitselfw )



- - Un nouveau manager

Cliquez ici pour ouvrir une fenêtre si ce n'est déjà fait. sur MyPitSelf et connectez vous en tant qu'utilisateur/mot de passe = 1/1
Il faut maintenant créer un manager qui aura la possibilité d'utiliser ce nouvel environnement.
Il y a trois profils de base dans un développement avec MyPitSelf:
  • Le super utilisateur qui peut tout faire.
  • Le développeur qui ne peut pas créer des éléments mettant en cause l'architecture du système à développer.
  • Le manager qui peut faire pas mal de choses.
Pour le créer, il faudra exécuter les actions que vous verrez en cliquant ici

Un développeur de type manager est créé. Son nom est m, son mot de passe est 1.
On lui a donné l'autorisation de travailler sur l'environnement de test.
Le champ contenant l'Ordre de l'environnement permet de fixer l'environnement par défaut ( ordre le plus petit ) de l'utilisateur quand il se connecte.




- - Commencer à développer


- - - Initialiser l'environnement

Cliquez ici pour ouvrir une nouvelle fenetre si ce n'est déjà fait
Quittez l'environnement principal si vous êtes connecté en tant qu'utilisateur 1/1
et connectez vous en tant que m/1

Comme vous vous êtes connecté en tant qu'utilisateur m, vous ne voyez pas les mêmes menus que précédamment, en particulier, les menus permettant de gérer les utilisateurs sont réservés au super utilisateur.

En revanche d'autres menus sont apparus et vous permettront de développer.
Il faut tout d'abord créer une image de la base de donnée. En fait cette image a déjà été créée lors de la création de l'environnement. Vous pouvez la voir en cliquant sur le bouton base du menu.

Puis il faut donner à cette image une existance réelle c'est à dire créer réellement la base dans MySql.
Pour ce faire, exécutez les actions que vous voyez en cliquant ici .

Remarque: La base contient déjà 2 tables: Une table utilisateurs et Lock ( verrous ).
Ces deux tables sont créées car MyPitSelf est conçu pour travailler en environnement multi utilisateur.
Elles peuvent être supprimées ou modifiées mais pour l'instant, nous n'allons pas y toucher.


- - - Créer une table

Puis il faut créer la table des tâches. Cette table est rattachée à la base et contient :
  • Un champ ID = identifiant unique de chaque tâche
  • Un champ Nom = nom de la tâche sur 64 caractères non vides
  • Un champ priorite = priorité de la tâche numérique entier
  • Un champ utilisateur = Référence de l'utilisateur créant la tâche


Des types de données existent déjà, nous allons les utiliser sans créer des nouveaux types. Voir le bouton donnee du menu.

Pour ce faire, exécutez les actions que vous voyez en cliquant ici .



- - - Créer une requête liste

Maintenant il faut créer une requête permettant de lister les tâches. Elle resemblera à:
SELECT T0.ID,T0.Nom,T0.Priorite,T0.xRefUser  
FROM
$Data.Taches T0 
WHERE T0.Priorite <= %SQLPAR0% and
      T0.Nom LIKE  '%%SQLPAR1%%'  and
      T0.xRefUser = %SQLPAR2%   
ORDER BY 
      T0.Priorite DESC ,
      T0.ID DESC  
LIMIT $xxxlistStart , $xxxlistCount


%SQLPAR0% et %SQLPAR1% sont les deux critères de recherche possibles des tâches,
%SQLPAR2% limite la recherche à un utilisateur donné.

MyPitSelf contient un utilitaire permettant de générer les requêtes sur la base de donnée.

Nous allons l'utiliser pour créer une requête de type SELECT LIST.
Ce type de requêtes a été défini car il permet de générer des instructions propres à l'affichage d'une liste.

Pour l'utiliser, exécutez les actions que vous voyez en cliquant ici .

Le résultat de cette action, visible en cliquant sur le menu Requête est une requête SQL ayant un Code 1201 dans notre cas.

Nous avons coché le champ Hidden et Constant car lors de l'affichage de la liste des tâches, il ne faut pas afficher l'ID utilisateur ( c'est l'utilisateur en cours ) et le critère d'affichage des tâches est pour l'utilisateur en cours donc constant.



- - - Créer un menu

Maintenant, il faut créer un menu permettant d'afficher la liste des tâches.
Ceci passe tout d'abord par la création d'une fonction écrite en langage MyPitSelf.
Cette fonction sera ensuite attachée à un menu.
Pour ce faire, exécutez les actions que vous voyez en cliquant ici .
Utilisez un copier/coller pour éviter de réécrire la fonction.
Un petit mot ici sur le langage MyPitSelf puisque cette fonction a été écrite dans ce langage:
Cette fonction permettant d'afficher la liste des tâches de l'utilisateur
Tac(
 Cst(999999999),    // paramètre priorité de la tâche
 Cst(),             // paramètre Nom de la tâche ( chaine vide )
 VarG(xxUserCode), // Code de l'utilisateur ( variable globale )
 Cst(3),            // 3 paramètres
 a0_list(           // afficher la liste basée sur ...
  Sql(1201)         // ... la requête 1201
 )
)
veut dire:
Tac ==> Take and Call: c'est l'INSTRUCTION principale de ce langage: Elle permet
  • soit d'ajouter des valeurs dans la pile d'appel
  • soit d'appeler des fonctions
Ici, on met des constantes dans la pile d'appel ( 999999999, chaine vide et la valeur 3)
On met aussi une variable globale initialisée par le système. Elle représente l'unique ID de l'utilisateur.
La fonction appelée est a0_list avec un paramètre qui est le numéro de la requête à appeler.

L'instruction Tac sait reconnaître les fonctions permettant de mettre des variables dans la pile et celles exécutant des actions.
Ce langage est basé sur des fonctions qui peuvent être imbriquées les unes dans les autres ou appelées séquentiellement.
cette fonction peut aussi s'écrire de la façon suivante:
Tac( Cst(999999999), Cst(), VarG(xxUserCode), Cst(3), a0_list(  Sql(1201) ) )
La fonction a0_list par définition prend 1 ou plusieurs paramètres dans la pile de variables, le premier paramètre ( 3 dans notre cas ) indiquant le nombre de paramètres à prendre dans la pile ( 99999, chaine vide, code utilisateur.

Vous avez compris que // indique un commentaire.



- - - Créer les fonctions

Maintenant, il faut créer une fonction permettant d'ajouter une tâche.

Nous allons utiliser l'outil pour créer une requête de type SELECT VALUE.
Ce type de requêtes a été défini car il permet de générer des instructions propres à l'affichage d'une entrée dans la base. Pour ce faire, exécutez les actions que vous voyez en cliquant ici .

Enfin, nous pouvons créer la fonction de type LISTTOP rattachée à la liste affichant la requête 1201
Pour ce faire, exécutez les actions que vous voyez en cliquant ici .

Enfin la dernière étape sera de regénérer toutes les requêtes, de compiler toutes las fonctions, d'établir la langue par défaut du programme et de créer les utilisateurs initiaux ( 0 = tous, 1 = super user )
Pour ce faire, exécutez les actions que vous voyez en cliquant ici .

Finalement, on peut commencer à tester l'application qui contient maintenant quelque chose.


- - Tester


- - - Ajouter des tâches

Cliquez iciet choisisez le bouton Test pour ouvrir une fenêtre sur votre nouveau programme en vous connectant avec le nom/mot de passe 1/1.

Maintenant, vous êtes le super utilisateur du NOUVEAU PROGRAMME, vous pourrez définir les autres utilisateurs et leurs droits tout comme vous avez défini un nouvel utilisateur dans l'environnement principal.

Tout d'abord, il faut savoir que par défaut, le super utilisateur est aussi un utilisateur normal et peut avoir accès à tous les menus.Si lors de la création du menu ( plus haut ) , vous ne cochez pas la case "pas pour super", le menu est disponible pour le super utilisateur.

Maintenant, nous allons tester le menu tâches en cliquant ici .




- - - Ajouter des utilisateurs

Puis nous allons ajouter deux utilisateurs en les rattachant à des groupes d'utilisateurs manager et utilisateurs qu'il faudra ensuite créer dans l'environnement de développement.
Pour créer ces utilisateurs, exécutez les actions que vous voyez en cliquant ici .




- - - Ajouter les groupes

Puis nous allons définir deux groupes d'utilisateurs ( manager et utilisateurs ) dans l'environnement de développement
Les actions à faire sont celles que vous obtenez en cliquant ici .

Pour leur assigner des menus, les actions à faire sont celles que vous obtenez en cliquant ici .


- - Ajouter des fonctions

Revenez dans l'environnement de développement de MyPitSelf en cliquant ici et connectez vous en tant que m/1.

Nous allons ajouter des fonctions qui permettent de voir, de modifier, de dupliquer ou de supprimer une tâche.

Pour la supression, il faudra tout d'abord créer une requête de type DELETE VALUE. Retenez le numéro de requête créé, nous nous en serviront dans les fonctions.
Pour ce faire, exécutez les actions que vous voyez en cliquant ici .

Puis nous crérons les fonctions de type LISTFUNCT.
Pour ce faire, exécutez les actions que vous voyez en cliquant ici .


Maintenant, nous allont tester les nouvelles fonctions; allez dans l'environnement test et connectez vous en tant que m/1
puis testez en exécutant les actions que vous voyez en cliquant ici .

Voilà, la première partie de l'aspect organisationnel est fini. Nous allons maintenant créer la fonction permettant au manager de voir les tâches de tout le monde.



- - Une nouvelle requête.

Maintenant, nous pouvons faire un menu permettant de voir les tâches de tout le monde. Ce menu sera pour les managers.
Pour celà, il faudra créer une requête pour lister les tâches et le nom de la personne ayant créée la tâche doit apparaître.
Cette requête aura deux tables: la table des utilisateurs et la table des tâches.
Exécutez les actions que vous obtenez en cliquant ici .

Puis il faudra créer un nouveau menu.
Exécutez les actions que vous obtenez en cliquant ici .


Puis reconnectez vous en tant qu'utilisateur m/1, ce menu est apparu et vous pouvez le tester en exécutant les actions que vous obtenez en cliquant ici .


- - Tables liées

Le client veut ajouter des fonctionnalités:
A partir de la liste des toutes les tâches, le manager doit pouvoir:
Créer une tache pour un autre utilisateur que lui même.
Modifier une tâche d'un utilisateur et en particulier, la réaffecter à un autre utilisateur.

Comment mettre celà en place ?

Je pense que vous avez compris que MyPitSelf utilise des tables liées.
Par exemple, pendant le développement, vous avez remarqué qu'on rattachait un menu à une fonction, une table à une base, un champ à une table, un menu à un groupe d'utilisateur, etc ....
Nous allons créer un lien.

Le lien qui nous concerne ici est qu'une tâche est rattachée à un utilisateur particulier.
Lorsqu'un manager doit affecter la tache d'un utilisateur "A" à un autre utilisateur "B", il doit pouvoir choisir l'utilisateur dans la liste des utilisateurs existants, c'est à dire dans la table des utilisateurs.

Ce lien existe déjà partiellement car lors de la création de la table des taches, nous avons spécifié que le champ xRefUser pointait sur le champ code de la table des utilisateurs.
En revanche, la fonction associée était bidon ( dummy ). Maintenant, nous allons créer une vrai fonction.
Ceci passe par la création
  • d'une liste d'utilisateurs ( Select du type SELECT LIST ),
  • D'une fonction de type XREF faisant référence à cette liste.
  • d'un lien entre la table des tâches ( champ utilisateur ) et la table des utilisateurs ( champ ID ) en utilisant cette fonction ( Une tâche est rattachée à un utilisateur mais un utilisateur n'a pas obligatoirement des taches )


Connectez vous à MyPitSelf en tant que m/1 et exécutez les actions que vous verrez en cliquant ici .

Puis il faut créer les fonctions pour le manager.
  • d'une requête de type SELECT VALUE permettant au manager de modifier une tâche.
  • d'une fonction en face de chaque tâche avec le type LISTTOP permettant de la modifier.
  • Une fonction avec le type LISTTOP pour créer une nouvelle tâche.

Connectez vous à MyPitSelf en tant que m/1 et exécutez les actions que vous verrez en cliquant ici .

Ceci fait, Connectez vous à au programme test en tant que m/1 et exécutez les actions que vous verrez en cliquant ici .

Puis nous allons ajouter la fonction ( type LISTTOP ) permettant de créer une nouvelle tâche, connectez vous à MyPitSelf en tant que m/1 et exécutez les actions que vous verrez en cliquant ici .

Ceci fait, connectez vous à votre application en tant que m/1 et exécutez les actions que vous verrez en cliquant ici .

Maintenant, connectez vous à votre application en tant que u/1 et vous pourrez constater qu'il existe une tâche affectée.



- - Conclusion

Tout cet exemple vous a montré comment on développe avec MyPitSelf ainsi qu'une partie des fonctionnalités.

Ici, l'approche a été très détaillée et en pratique, il existe des raccourcis pour créer les éléments ( requêtes, fonctions, menus ... ) automatiquement.
En fait le développement de cette application ne devrait pas prendre plus de 15 minutes.

Le livrable se situera finalement dans le répertoire "prod" après avoir utilisé la fonction d'extraction ( voir menu outil / Créer les programmes dans l'environnement externe )

Un des intérêts d'avoir un environnement de production séparé de l'environnement de développement est que ce dernier peut contenir des fonctionnalités du type mise à jour de libellés en ligne.
C'est pour cela que dans le menu profil, il existe le bouton permettant de basculer le mode de déboggage. Quand ce dernier est actif, on peur suivre à la trace tous les appels de fonctions.

Comme MyPitSelf est fait en majorité avec lui même, le meilleur exemple de développement que vous pourrez trouver s'obtient en vous connectant en tant qu'utilisateur 1/1 à l'environnement de développement.
Puis cliquez sur le menu "Outil", puis cliquez sur le bouton "Basculer le mode de développement de MyPitSelf".
Tous les menus apparaissent alors et vous pourrez voir pas mal de choses.
Vous pouvez aussi utiliser le bouton "Cacher ou voir les appels de fonctions" dans le menu profil qui permet de voir quelles fonctions/requêtes sont utilisées.

Un trait essentiel de MyPitSelf est que les fonctions écrites dans son langage sont décomposées en unités syntaxiques qui sont visualisable en utilisant le bouton imprimer en face des sources des fonctions dans la liste des fonctions.
Le résultat de cette décomposition est une grille qui peut être mise dans une table de base de donnée.
Les tuples de cette table peuvent être analysés et modifiés par des fonctions de bases de données.
On peut exécuter des fonction de reverse engineering sur cette forme tabulaire puis regénérer le code source à partir de cette forme tabulaire .


Voyez aussi les chapitres suivants pour en apprendre un peu plus sur MyPitSelf

Je n'ai pas encore parlé de beaucoup de choses, il faut que j'enrichisse encore la documentation, mais je pense que vous les avez plus ou moins présenties.
MyPitSelf va encore évoluer car il y a des trous fonctionnel, mais étant donné que presque tout est en base de donnée, y compris les programmes, que les programmes sont liés aux données et que les programmes sont en fait que des données à traiter, tout ce qu'il y a à faire est d'ajouter les données pour combler ces trous.

Quand on doit gérer des modèles de données un peu complexes (voir par exemple au hasard, celui de MyPitSelf présent sur la page d'accueil), c'est bien d'avoir un outil que facilide la tâche.

Je trouve que MyPitSelf, c'est pas mal pour gérer des modèles avec beaucoup de liens .


Langage


- Généralités

The MyPitSelf language evolves constantly because of its own concept and because improvements are brought, but it will remain with only function calls returning true or false.

It is a "blending" of
lisp for the nested treatments and true or false return values
C for the function calls
Assembler for the stack mechanism

The best method to learn this language is like for all languages:
- Look and do the development examples given above, this will make you familiar to the basic concepts and the development environment.
- See examples of this language. for this, go to the MyPitSelf environment as user 1/1 and to choose in the tool menu the button "Set or unset Developping mode for MyPitSelf" and to look at the functions.

This function refererence is mostly done so that when I need to write some source, I can copy the example and then paste it in the source.

When you are in the function list, the button gives a result under a table view.
In other words, if a function is f(g(x),h(y)), the table result of this function is

Function# Parent
Number
Order In
Parent
Function
Name
Function
Type
Number Of
Children
Level
0 -1 0 0 INIT 1 0
1 0 1 f formula 2 1
2 1 1 g formula 1 2
3 2 1 x zone 0 3
4 1 2 h formula 1 2
5 4 1 y zone 0 3

With these data, a procedure can implode it to rebuilt the original string [ f(g(x),h(y)) ].
This means that if some modification are done on the language structure, by putting this table in a database table and working on it ( ie add/move/suppress records ), one can make the functions evolve.
For example, like in the functions attached to a list, I decided once to put the field specification inside a Field() function.
The old format was:
Tac(
 T0.ID,
 doSomething(),
)
And the new format is:
Tac(
 Field(T0.ID),
 doSomething(),
)
To do this, I wrote functions that did that job.
That is the reason why even if I know that certains things have to be corrected, I do not panic about correcting all the sources.

Furthermore, the possibility to put the function in tables helps to trace every use of data. As the SQL phrases are written in database in an organized way and then translated into languages functions, one can know which data is used where.

The use of this possibility is for the moment in only few places but will be generalized to have a trace of everything.

Some functions have no arguments, some others fixed arguments, some others fixed functions arguments and other variable functions arguments.
A function always return true or false.

The arguments of the functions are inclosed in parenthesis and separated by comas.

Line comments start with // and they end with the end of line.
Block comments start with /*, end with */, these delimiters must be on the first column and can be nested.
The ' is a string delimitter, the escape character is \


- Variables

Variables can contain any basic type of data ( strings, dates, times...) but not yet arrays
There are two sets of variables.
Some are declared and named ( see SetVar below ) and are contained in a stack: It's the variable stack.

Others are put in a stack by programs. It's the Call Stack.
As MyPitSelf is based on data, some instructions extract data from the databases and puts them in the call stack.

The way to reach the variables put in the call stack is by giving a number which represents the position in the call stack:
If the call stack contains ( it is filled going downwards )
hello
world

1 represents world
2 represents hello

Some variables are globals ( particulary predefined variables )

The hw() function displays the content of the call stack ( "hello, world" )
the pvs() function displays the content of the variable stack. ( print variable stack )


- - SetVar

SetVar(xxxx,yyyy[,yyyy]) for local variables
or
SetVarG(xxxx,yyyy[,yyyy]) for global variables

Where xxxx can be:
a variable name ( no spaces or special characters admitted )
Var(xxxx) : The content of the variable whose name is xxxx. ( equivalent to the $$ in php ).

yyyy can be:
-Var(zzzz) or VarG(zzzz): The content of the variable zzzz, zzzz must exist.
-Chr(n): where n is the ascii code of a character. It is useful for specials characters like Chr(10) or Chr(13)
-Cst(xxxx) : A constant, it can be a string or a number.
-n: A number which represents the niest last character of the CallStack.
-Display(nnnn) : nnnn is the message number in the msg list ( usefull for internationalisation )
-Funct(nnnn) : nnnn is a function number: It replaces the Cst(nnnn) because if someone give a new number mmmm to nnnn, all Funct(nnnn) will be moved to Funct(mmmm).
-Stack(oooo) : oooo is a name of a variable which contain the position of the element to fetch in stack.
-SessionVar(ssss) : ssss is a name of a constant to get in $_SESSION .
-HttpPostVar(pppp) : pppp is a name of a constant to get in $_POST .
-Num(qqqq,x) : qqqq is a name of a variable, x is the number of decimals.
-Env(rrrr) : rrrr is a constant representing the environment variable to get ( php getenv() )
-XVS(zzzz) : gets the content of the variable which is in the Variable stack ( zzz is the variable name )

Examples:
SetVar(MyVar01,Cst(Hello),Chr(39),Chr(32),Cst(world)), // puts "hello, world" in MyVar01.
SetVar(MyVar01,Cst(25.5)),                             // now, it contains 25.5
SetVar(MyVar01,Cst(0)),                                // now, it contains 0
SetVar(MyVar02,Cst(ACombinedvar),Var(MyVar01)),        // MyVar02 contains ACombinedVar0
SetVar(Var(MyVar02),Cst(25.5))                  // ACombinedVar0 is created as a new variable
                                                // and contains 25.5
SetVar(toto,Display(1000011)) // puts the content of the message n# 1000011 in toto



- - Variables prédéfinies

An environment has it's own variables like
the folder of it's programs
The user code of the current user,
Default values for data ....

Go to the profile menu and use the "see/hide function call" button to see function calls.
Then a button appears on the main menu ( print variable stack ).
These predefined variables can be seen using this button. They start with xxx.
These variable are in the variable stack just like the ones the ones declared with SetVarG.
The pvs() function can be used to display variables in a program.

For the moment, one can update these variable which is absolutely not correct and has not to be done but this will be enhanced to have more control on the use of the "system variables" according to the user profile.


- - Opérations sur les variables

They are basic because most operations are done using values in the call stack.

AddVar(xxxx,yyyy)
Adds to xxxx ( must exist ) yyyy, yyyy can be: Cst(), Var(), n ( a value in the call stack )

SubVar(xxxx,yyyy)
Subs to xxxx ( must exist ) yyyy, yyyy can be: Cst(), Var(), n ( a value in the call stack )

MultVar(xxxx,yyyy)
Multiply to xxxx ( must exist ) yyyy, yyyy can be: Cst(), Var(), n ( a value in the call stack )

DivVar(xxxx,yyyy)
Divide xxxx ( must exist ) by yyyy, yyyy can be: Cst(), Var(), n ( a value in the call stack ) If yyyy = 0, it returns false.

RemainderVar(xxxx,yyyy)
xxxx % yyyy . If yyyy = 0, it returns false.

To test if a variable exist, use this kind of source :
Tac(
 Test(
  Cond(IsSet(Var(MyVar))), // or Cond(IsSet(VarG(MyGlobalVar)))
  IfTrue(
   // do something
  ),
  IfFalse(
   // do something else
  )
 )
)



- - Tac

Tac: pushes values into the callstack
Tac(
 Var(xxxx),             // xxxx is a variable name
 VarG(xxxx),            // xxxx is a variable name
 Cst(yyyy)              // yyyy is a constant.
 Chr(zzzz)              // zzzz is a numeric value for an ascii code
 Display(nnnn)          // nnnn is a message number.
 Field(Tn.FieldName)    // The FieldName can be use in LISTFUNCT functions, ie T0.Name
 Funct(oooo)            // oooo is the number of the function to call
 ffff(),                // ffff is a function to call whose name is not 
                        // one of the previous in this list
                        // The Function is called for execution
)


Example:
Tac(
 SetVar(MyVar,Cst(Hello,world)), // defines a variable "myVar" and puts hello, world in it
 Var(MyVar),                     // push the variable in the call stack
 out(1)                          // outputs the last variable in the call stack
)                                // all variables pushed in this Tac() are poped

The Tn.Field name must be at the level one of the function. In this case the variable is transformed into a constant. ( use the Show source of your browser to see how the FUNCTION field is filled in any list screen ).
So if a LISTFUNCT function is
Tac(
 Field(T0.ID),
 out(1)
)
and the result button is in front of the record ID 1234, the source will show something like:
onclick="document.XXFNCT.value='Tac( Cst(1234), out(1) )'"




- - Swap

Puts in the call stack the value of a variable.
It is usually used to return some values: as this language returns only true or false, if you want to get something different from a function call, you can use the call stack and put some values in it.
Example
// Receives the field code and builds the string to build the sql for this field
SetVar(FieldCode,1),

Tac(
 Var(FieldCode),
 R_1523_Select(
  Do(
   Take(T0.Name,T0.xRefData),
   SetVar(FieldName,2),
   SetVar(DataCode,1),
   Tac(
    Cst(0), // not system
    Var(DataCode)
    Funct(6091),  // builds for example ' varchar(50) NOT NULL
    SetVar(SqlString,Cst('`')Var(FieldName),Cst('`'),Ret(1))
    // now, SqlString contains `TheFieldName` varchar(50) NOT NULL
   )
  )
 )

 Swap(SqlString,1), // the function calling this one can do a
                    // SetVar(SqlField,Ret(1)) 
)



- - FillVar

Fills a variable with a value according to it's data type:
It is mostly used for ENUM and FLAG types
Example:
Tac(
 SetVar(DataType,Cst(1002)),
 SetVar(Value,Cst(1)),
 
 FillVar(Libelle,DataType,Value),  // Now Libelle contains <img src="cc.gif" />
 FillPgmVar(Libelle,DataType,Value),  // Now $Libelle contains <img src="cc.gif" />

 Ech(Var(Lib)),
)



- - DataVal

Tac(
 SetVar(ExternalRule,DataVal(InternalRuleName,Data(1034))),
 SetVar(out,Var(out),sp(),Var(ExternalRule),Cst(':')),
)



- Nombres


- - Formatter un nombre

To put a number in the local format, do:
Tac(
 SetVar(Value,Cst(1234.567))
 SetVar(DisplayValue,Num(Value,3)), // Display value contains 
                                    // 1 234,567 in french format 
                                    // 1,234.567 in us format
)




- - Arrondir un nombre

Tac(
 SetVar(R,Cst(0))
 SetVar(Value,Round(Value,R))
)



- tableaux

Contenu pour le chapitre tableaux


- - explode tab

Tac(
 SetVar(sep,Cst(',')),
 SetVar(value,Cst('hello,world')),
 SetVar(myTab,explode(sep,value)),
 SetVar(Co,1),
 SetVar(myVal,tab(myTab,Co)), // myVal contains "world"
)



- Contrôles et appels


- - Test

Tests a contition to execute or not execute a treatment
Test(
 Cond(xxx),
 [IfTrue(yyy),]
 [IfFalse(zzz),] 
)
where xxx is the condition: It can be a function ( and usually is ).
yyy is the series of function to do if the condition returns true
zzzz is the series of function to do if the condition returns false
If one or both of ReturnIfxxxx() are not mentioned, the returned value is true if the condition matches the ReturnIf ommitted.
Example 1:
SetVar(ValueToTest,1),
Tac(
 Var(ValueToTest),
 Test(
  Cond(Equal(0)),  // the function Equal return true if the last value in varstack = it's argument.
  IfFalse(
   Msg(This will never be displayed but the program continues)
  )
 )
)

The Equal function can take as argument:
a constant: can be a string or a number
Var(xxx): a variable whose name is xxx
f(): a function. Example 2:
SetVar(ValueToTest,Cst(1)),
Tac(
 Var(ValueToTest),
 Test(
  Cond(Equal(1)),  
  IfTrue(
   Msg(I do not want to continue so I exit the program ),
   ReturnFalse() // this instruction stops the program returning false
  )
 )
)

Example 3:
SetVar(ValueToTest,Cst()),
Tac(
 Var(ValueToTest),
 Test(
  Cond(IsVoid()), 
  IfTrue(
   Msg(I DO want to continue  ),
  ),
  IfFalse(
   Msg(I DO NOT want to continue ),
   Test(
    Cond(Funct(123456)) // writes in errorlog something
    IfTrue(
     ReturnFalse() // if we can't write in error log, BIG ERROR
    )
   )
  )
 )
)
Test a function result:
Tac(
 Var(TableCode),
 Var(FieldCode),
 Test(
  Cond(Funct(Var(T0TestFunct))), // Var(T0TestFunct) contains the function number
  IfTrue(
   Tac(
    // do something
   )
  )
 )
)



- - LoopVar

Loops n times a treatment.
LoopVar( 
 xxxx,
 yyyy, 
 Do(zzzz) 
)
where xxx is a variable containing the number of loops to do.
When the loop is executed, yyyy is set to the current loop number
zzzz is the series of functions

Example:
SetVar(ValueToLoop,3),
LoopVar(
 ValueToLoop,ind,
 Do(
  SetVar(Message,Cst('hello world '),Var(ind)),
  Tac(
   Var(Message),
   out(1)         // outputs the 1st value in the call stack
  )
 )
)
this loop displays hello, world 1 hello, world 2 hello, world 3


- - SwitchCase


Compares the last value in the call stack to some constants to execute functions
SwitchCase(
 x01,f01(),
 x02,f02(),
 .,
 .,
 .,
 [flast()]
)
x0i are constants, f0i are functions
If none of the constants are matched and there is NOT a last function, without a constant before, it returns false
If none of the constants are matched and there IS a last function, without a constant before, it executes this last function
Examples:
Tac(
 Cst(0),
 SwitchCase(
  0,  out(OK),
  1,  Tac(
       DoManyThings(),
       out(OK)
      ),
      out(KO) // default
 )
)
outputs OK and continues

Tac(
 Cst(2),
 SwitchCase(
  0,  out(OK),
  1,  out(OK),
      out(KO)
 )
)
outputs KO, and continues

Tac(
 Cst(2),
 SwitchCase(
  0,  out(OK),
  1,  out(OK)
 )
)
returns false ( it's a bug because the case has not been considered )



- - Funct

Funct(xxxx)
where xxxx is the number of the function to call
or xxxx = Var(yyyy) where yyyy is a variable that contains the function number to call


- - Comparaisons

Compares with the last value un the call stack
Equal(xxxx),
EEqual(FALSE),EEQUAL(NULL) // does a type check ( === in php )
Greater(xxxx),
Lower(xxxx),
LowerEqual(xxxx)
GreaterEqual(xxxx)
IsVoid()
IsNotVoid()
IsNumeric()
where xxxx is a constant or xxxx = Var(yyyy)
with yyyy = variable name

Example:
Tac(
 SetVar(src,Cst('this is a . bug ;')),
// SetVar(src,Cst('$temp="This correct";')),

 PhpTest(retour,src),

 Var(retour),
 Test(
  Cond(EEqual(FALSE)),
  IfTrue(
   out(KO),
   ReturnFalse()
  ),
  IfFalse(
   out(

OK) ) ) )



- Fichiers


- - a0_File


- - a0_WriteInFile

Writes the content of a variable in the variable stack into a file.
Example:
SetVar(FileName,Cst('helloworld.htm')), 
SetVar(FileContent,Cst('hello, world')), 
a0_File(
 FileName(FileName),
 FilePtr(fp),
 Action(w),
 Do(
  a0_a0_WriteInFile(FileContent,fp) 
 ) 
) 
this writes a file "helloworld.htm" containing the string "hello world".


- - a0_WriteInSourceFile

Writes the content of a variable in the variable stack into a file adding a Begin Php Tag at the begining of the file and a End Php Tag at the end of the file.
Example:
SetVar(FileName,Cst('helloworld.php')), 
SetVar(FileContent,Cst(' echo "hello, world\\n" ;')), 
a0_File( 
 FileName(FileName), 
 FilePtr(fp), 
 Action(w), 
 Do(
  a0_WriteInSourceFile(FileContent,fp)
 ) 
), 



- - a0_ReadFileInVarStack

Read the content of a file and put it in VarStack. The syntax is:
a0_ReadFileInVarStack(
 xxxx,
 yyyy
)
where
xxxx is the variable name which will contain the content of the file
yyyy is the variable name which contains the name of the file pointer
Example: To copy a file a into a file b, use this
a0_File(
 FileName(SourceFile), // complete path 
 FilePtr(sfp),
 Action(r),
 Do(
  SetVar(FileContent,Cst()),
  a0_ReadFileInVarStack(FileContent,sfp),
  a0_File(
   FileName(TargetFile), // complete path
   FilePtr(tfp),
   Action(w),
   Do(
    a0_WriteVarStackInFile(FileContent,tfp),
   )
  )
 )
)



- - a0_WriteInBinaryFile

Writes a file content as binary data, useful for copying images.
example: Copy the content of img directory which contains images
SetVar(FilePath,VarG(xxPaWr),VarG(xxDatB),Cst(/../)), 
SetVar(FileName,Cst(^.*$)), 
Tac( 
 Var(FilePath), 
 Var(FileName), 
 a0_DirList( 
  SetVar(FileName,3), 
  SetVar(FileSize,2), 
  SetVar(FileTime,1), 
  SetVar(SourceFileContent,Cst()), 
  SetVar(SourceFile,VarG(xxPaWr),VarG(xxDatB),Cst(/../),Var(FileName)), 
  SetVar(TargetFile,VarG(xxPtaW),Cst(../),Var(FileName)), 
  a0_File(
   FileName(SourceFile),
   FilePtr(SourceFp),
   Action(rb),
   Do( 
    a0_ReadFileInVarStack(SourceFileContent,SourceFp),
    a0_File( 
     FileName(TargetFile),
     FilePtr(TargetFp),
     Action(wb),
     Do(
      a0_WriteInBinaryFile(SourceFileContent,TargetFp)
     )
    )
   )
  )
 )
) 



- - a0_Directory

Creates or delete a directory, the syntax is:
a0_Directory(x,path)
Where x = c ( create ) or d ( delete ).
and path is the variable containing the path of the directory
When the directory is deleted, all the files included are deleted but not the sub dir.

Example
Tac(
 SetVar(TargetBackup,VarG(xxPtaB),LowerCase(SystemDbToBackUp)),
 a0_Directory(c,TargetBackup),
)



- - a0_DeleteFile

deletes a file.
The syntax is:
a0_DeleteFile(xxxx) 
Where xxxx = Var(yyyy), yyyy is a variable contaning the name of the file to delete or xxxx = the name of the file


- - a0_DirList

Puts in the call stack a list of files in a directory according to a file name ( ereg function )
Example: to list the skin files, the function used in MyPitSelf is:
SetVar(FilePath,Cst()),                 // The path is void ( current path )
SetVar(FileName,Cst(^styles.*\php$)),   // file name looks like styles_*.php
Tac(
 Var(FilePath),
 Var(FileName),
 SetVar(CurrentSkin,Display(1000063),VarG(xxUserSkin),Cst(
)), Ech(Var(CurrentSkin)), // the current skin is: a0_DirList( SetVar(FileName,3), SetVar(FileSize,2), SetVar(FileTime,1), SetVar(Button,Funct(239)), Tac( Var(FileName), Cst(1), Cst(OPTION), // CLASS of the button Cst(.), // Image Var(FileName), // TITLE (Tool tip of the button) Var(FileName), // text displayed on the button Var(Button), // Function number to call Funct(1001), SetVar(Button1,1,Cst(

)), // Put the html code in a variable to display it ), Ech(Var(Button1)) // displays the button ) )



- - a0_DirDirList

List directories of a directory.
Example: The .css files are created in the MyPitSelf environment. After their creation, they must be copied in the other programs so this function is used:
Tac(

 SetVar(DirPath,VarG(xxPaWr)),
 SetVar(Patern,Cst('^.*$')),

 Var(DirPath),
 Var(Patern),
 a0_DirDirList(
  SetVar(DirName,2),
  SetVar(DirTime,1),
  Tac(
   Var(DirName),
   Test(
    Cond(Equal(VarG(xxVers))),
    IfFalse(
     Tac(
      SetVar(FromDir,VarG(xxPaWd),Cst('')),
      SetVar(ToDir,VarG(xxPaWr),Var(DirName),Cst('/')),

      SetVar(FileName,Cst('^a0_styles_[0-9]*\\..*s$')), // .js or .css

      Cst(0),        // silent does not echo the copy if 1
      Var(FromDir),
      Var(ToDir),
      Var(FileName),
      a0_CopyFileDir(),

     )
    )
   )
  )
 )
)



- - a0_CopyFileDir

Copy file matching a given pattern from a directory to an other one.
Example: The .css files are created in the MyPitSelf environment. After their creation, they must be copied in the other programs so this function is used:
Tac(

 SetVar(DirPath,VarG(xxPaWr)),
 SetVar(Patern,Cst('^'),VarG(xxVers),Cst('.*$')),

 Var(DirPath),
 Var(Patern),
 a0_DirDirList(
  SetVar(DirName,2),
  SetVar(DirTime,1),
  Tac(
   Var(DirName),
   Test(
    Cond(Equal(VarG(xxVers))),
    IfFalse(
     Tac(
      SetVar(FromDir,VarG(xxPaWd),Cst('')),
      SetVar(ToDir,VarG(xxPaWr),Var(DirName),Cst('/')),

      SetVar(FileName,Var(ToDir),Cst('mps_style_*.*')), 
      a0_DeleteFile(Var(FileName)), 

      SetVar(silent,Cst(0)),        // silent does not echo the copy if 1

      SetVar(FileName,Cst('^mps_style_.*s$')), // .js or .css
      a0_CopyFileDir(FileName,FromDir,ToDir,silent),

      // ..... etc

     )
    )
   )
  )
 )
)



- - FileExist

Check if a file exist, must be put in a test
 SetVar(FileName,Cst('file/'),Var(T0name),Cst('.png')),
 Var(FileName),
 Test(
  Cond(FileExist()),
  IfTrue(
   Tac(
   )
  ),
  IfFalse(
   Tac(
   )
  )
 ),
   



- Fonctions interactives


- - a0_list

Displays a list of values obtained from an SQL request.
The syntax is
a0_list( 
 Sql(xxxx), 
 [ShowVar(zzzz),] 
 [FieldToUpdate(iiii),FieldToSee(jjjj),] 
 [Header(Funct(kkkk)),Line(Funct(llll)),]
 [NoCounter(),] 
 [Hidden(Var(mmmm)),] 
 [Range(Var(nnnn))]
) 
where xxxx is the number of the sql request. It must be a SELECT LIST type.

zzzz is a variable which contains a string to display

a0_list is also used when there is a value to fetch in an other table ( see the XREF functions ). In that case iiii is the name of the field to fetch ( generally, T0.ID )
jjjj is the name of the field to see ( generally T0.Name )

kkkk is the number of a MyPitSelf function called to output the line header of the table
llll is the number of a MyPitSelf function called to output each line
nnnn is the number of a MyPitSelf function before displaying the list
oooo is the number of a MyPitSelf function after displaying the list
nnnn is the first line to display

Important point begin:
A request for a list has generally some input criterias. The todo request for example is
SELECT T0.Code,T0.Priority,T0.Name,T0.TimeStampCreate,T0.TimeStampUpdate 
FROM   $xxDatB.yTodo T0 
WHERE T0.User = %SQLPAR0% and T0.Priority >= %SQLPAR1% and 
      ucase( T0.Name ) LIKE ucase( '%yDoc%' ) and T0.Code <= %SQLPAR3% 
ORDER BY T0.Priority ASC , T0.Code DESC LIMIT 30 
The values %SQLPARn% are the input criterias for the todo list. These values must be put in the call stack before the call to a0_list and you must add the number of parameters of the request as the last value in the call stack.

Example: for the request 1001, the use of this function is:
Tac( 
 Var(xxUserCode), // parameter 1: user code ( %SQLPAR0% ) 
 Cst(0),           // parameter 2: priority ( %SQLPAR1% ) 
 Cst(),            // parameter 3: content of the todo ( yDoc ) 
 Cst(999999999999999999), // parameter 4: code of the todo ( %SQLPAR3% ) 
 Cst(4),           // There are 4 parameters. 
 a0_list( 
  Sql(101)
 ) 
) 
Important point end:

Some properties for a request list type can be added, they are:
SELECT DISTINCT add a "DISTINCT" in front of the select
LIST LIMIT Fixees the number of record to be output
Line height Line height in pixel of the list
Cell padding Cell padding in pixelx of the values in the list
OPTION COLUMN WIDTH width of the column that contains the option buttons
Do not put the list in a table Normally, a list is put in a table, setting this flag to 1 prevents it.
GoTo buttons in a list Add one more access buttons in a list
Line function Reference of the line function to use to display the data
Header function
NoMenu
Before list function
After list function
SetVar(ToDisplay,VarG(xxxHeader)), 
Tac(
 Var(ToDisplay),
 Cst(<), CutWithoutLastCharEqual(), // take off </td>
 SetVar(ToDisplay,1,Cst(<td>Stack</td></tr>)),
 Ech1(Var(ToDisplay))
)
An example of a Line function is
Tac( // Line function of list of 1453

 SetVar(Option,VarG(xxxOptionLine)),
 
 SetVar(Button,Funct(5574)), 
 VarG(T0Code),
 Cst(1),
 Cst(.),              // CLASS 
 Cst(u.gif),          
 Display(2000215),
 Display(2000214),    // text displayed on the button
 Var(Button),         
 Funct(1001),
 SetVar(Option,Var(Option),1),

 SetVar(ToDisplay,VarG(xxxStartLine),Var(Option),Cst(</td>),VarG(xxxValueLine),Cst(</tr>)),
 Ech1(Var(ToDisplay))
),


NoCounter() : The and buttons to see the next records and the counter (xxx->yyyy)/zzz on the top of the list are not displayed

Hidden(Var(mmmm)) : A variable is put in the Variable stack with the name xxx



- - a0_Screen


Display a record on the screen for Create, View, Update or duplicate.
The syntax is
a0_Screen(
 Mode(xxxx),
 Sql(yyyy),
 [ShowVar(zzzz),]
 [After(Funct(iiii))|ScreenAfter(Funct(iiii),]
 [Replace(T0.jjjj,Var(kkkk)),]
 [NoLock(),]
 [NoLog(),]
 [Under(Funct(llll))]
)
where
xxxx = Create or View or Update or Duplicate: Action to do with the record
yyyy = the sql number to use
zzzz = name of the variable containing a string to display
iiii = Function number to call after an Update, a Duplicate or a Create.
The difference between After() and ScreenAfter is the type of the function iiii.
The ScreenAfter function usually ask some data .
Example:
When the super user creates a new user, the next screen that comes after the "CREATE" button is the one that ask the user password which is function # 4021, so it gives:
a0_Screen( 
 Mode(Create), 
 Sql(1014), 
 ScreenAfter(Funct(4021))
)

jjjj,kkkk if some fields are hidden in the Sql request, this lets you fill the field with a value.
NoLock(): Usually, an update must lock the record so that someone else can't modify it. But in certain cases, the lock is not wanted.
NoLog(): Usually, a View option is logged in the system so that when you click on the Back button, you can see the screen again. But, in certain cases, the loggin must not be done so you can use this verb.
Under(Funct(llll)): where llll is a function number: If the Mode() type is update or View, llll represents a function that is called after displaying the screen. This last function receives as a parameter the ID of the record being displayed.

The values of the parameters must be pushed in the call stack before this call.


Example: the request (1002) to get a todo record by Code is:
SELECT  T0.Code,T0.Priority,T0.Name   
FROM 
 $xxDatB.yTodo T0 
WHERE 
 T0.Code = %SQLPAR0%
Note that the T0.Code, the auto incremented field is hidden ( go to the Request menu for select 1002, click on the Fields button, and View the T0.Code Field ).
So the creation of a todo record looks like:
a0_Screen( 
 Mode(Create),
 Sql(1002)
)

The update of a todo record looks like:
Tac(
 Field(T0.Code),        // first and only parameter of the request
 a0_Screen( 
  Mode(Update),
  Sql(1002),
  NoLock()
 )
)

Note: If a function is attached to the main table of the request then this function is called before the Update/create/Duplicate of the record.

The fields in these functions are available as VarG ( global variables ) and are referenced as TnFieldName WITHOUT a . between Tn and FiendName.
This comes because it's the value from the screen which is tested and not the value from the table.

See the TABLETEST functions for more examples.


- - a0_AskData

Interactive input ask to the user
Syntax:
a0_AskData(
 [Display(xxxx),]
 LaunchButton(Display(ZZZZZZ)),
 Par(yyyy,zzzz,Default(Var(kkkk)),OnUpdate(Funct(1234)),[,,,]
 [LaunchButton(llll),|NoLaunchButton()] 
 Tac(
  SetVar(Value0,Input(0)),[,,,]
  ffff()
 )
)

where

xxxx: can be a number and it displays the message number xxxx or Var(VariableName) ant it displays the content of the variable VariableName

llll = Display(iiiii) or Var(eeeee) to display a different text on the launch button than "GO"

yyyy: a string and if equals to Display(nnnn) it displays the message number nnnn

zzzz: either Data(iiii) where iiii is a number of a data in the Data menu
or a function to get the value from a table like

Tac(
 Cst(i),[,,,] Cst(j),
 a0_list(
  Sql(llll),
  FieldToUpdate(T0.FieldToUpdate),
  FieldToSee(T0.FieldToSee)
 )
)

(see the a0_list function for the syntax )


kkkkk is the name of a variable for an initial value

To get the parameters entered, use the syntax:
SetVar(Par0,Input(0)),
SetVar(Par1,Input(1)),
....

See below for an example.


This command can also be used to upload a file. In this case, 5 parameters are filled.
Example:
SetVar(idRoot,1),
a0_AskData(
 Display(2000409),
 Par(  Display(2000320),  Upload() ),
 Tac(
  SetVar(FileName,Input(0)),
  SetVar(FileType,Input(1)),
  SetVar(FileTmp,Input(2)),
  SetVar(FileSize,Input(3)),
  SetVar(FileErr,Input(4)),
  Var(FileErr),
  Test(
   Cond(Equal(0)), // no error
   IfTrue(
    Tac(
     Var(FileSize),
     Test(
      Cond(Lower(5000000)),
      IfTrue(
       // do stuff
      ),
      IfFalse(
       out(file greater than 5000000)
      )
     )
    )
   )
  )
 )
)


a big example I use for my own tests is:
SetVar(MonTest,Cst('hello, world')),
SetVar(hidd,Cst(1)),
SetVar(show,Cst(0)),
SetVar(MyDataType,Cst(14)),
a0_AskData(
 Display(2000276),
 LaunchButton(Display(2000340)),
 Par(Display(2000292),Data(Var(MyDataType)),Default(Cst(9999-12-31)),Hide(Var(show))),
 Par(Display(2000292),Data(21),Default(Cst(12:00:00)),Hide(Var(hidd))),
 Par(
  Display(2000292),
  Tac( 
   Cst(),Cst(INTEGER),Cst(0),Cst(9999-12-31),Cst(23:59:59),
   VarG(xxDefValDateTime),Cst(9999999),
   Cst(7),
   a0_list(
    Sql(1526),
    FieldToUpdate(T0.ID),
    FieldToSee(T0.Text)
    Data(22)
   )
  ),
  Default(Cst(0)),
  Hidden(Var(show)),
 ),
 Par(Display(2000292),Data(25),Default(Var(MonTest))), // html type
 Par(Display(2000292),Data(27),Default(VarG(xxDefValTimeStamp))),
 Par(Display(2000292),Data(9), Default(VarG(xxDefValDateTime))),
 Par(Display(2000292),Data(2), Default(Cst(1))),
 Par(Display(2000292),Data(26),Default(Cst(1235.02003))),
 Par(Display(2000292),Data(20),Default(Cst(1235.02))),
 Par(Display(2000292),Data(1002)),
 Par(Display(2000292),Data(1015)),
 Tac(
  out(liste des valeurs egal),
  out(11), out(10), out(9), out(8), out(7), out(6), out(5), out(4), out(3), out(2), out(1)
 )
)
Tac(
 Var(xxxShowForm),
 Test(
  Cond(Equal(1)),
  IfTrue(
   Tac(
    // if form display
   )
  )
 )
)




- - out, Display, Ech

out(n) // outputs the n'iest last value in the call stack ( for debug purposes )
out(bla bla) // outputs bla bla
out() // outputs a line break
ShowMsg(n) // outputs the message number n.
Ech(Var(MyVar)) // outputs the string contained in MyVar in span LISTTITLE attribute.
Ech1(Var(MyVar)) // outputs the string contained in MyVar without any HTML attribute.
Example, let's suppose that msg # 1000011 contains "hello, world"
Tac(
 Cst(Hello),
 Cst(world),
 out(2),            // outputs Hello
 out(1),            // outputs world
 out(hello world)   // outputs hello world
 ShowMsg(1000011)   // outputs Hello
 SetVar(TheMessage,Display(1000011)),
 Ech(Var(TheMessage)), // outputs Hello, world
)



- - nap

Delays the execution of the outputs of n seconds.
Syntax:
Tac(
 Cst(Wait for 2 seconds please), // message displayed
 out(1),
 Nap(2)  // 2 seconds 
)



- - Back, Home


Back(n) // Return to the n'iest previous screen.
Home() // go to the Home screen



- - menu

Displays the menu
For the Example: to display the documentation chapters in MyPitSelf, the function starts with
Tac(
 menu(),
 LogTheCall(),
 SetVar(NbTotChap,Cst(0))
 R_1798_Select(
  Do(
   Take(T0.Code),
   AddVar(NbTotChap,Cst(1))
  )
 ),
 ....
)
This function is usually used with the LogTheCall() function


- - LogTheCall

Usually, the calls to the functions happen only in list functions. But sometimes the menus call functions that are not lists.
If you wish to use the backs button in this case, use this function.
Example: to display the documentation chapters in MyPitSelf, the function 6770 starts with
Tac(
 menu(),
 LogTheCall(),
 SetVar(NbTotChap,Cst(0))
 R_1798_Select(
  Do(
   Take(T0.Code),
   AddVar(NbTotChap,Cst(1))
  )
 ),
 ....
)
This function is usually used withs the menu() function


- Chaînes de caractères


- - Conversion de chaines

As we use HTML, PHP, JavaScript containing HTML or Javascript containing Alert() function, many cases are possible because the string delimiter, the carriage return, the ditable fields can contain many cases.
Keep in mind that the string delimiter in MyPitSelf is '.
The ' can be put in a string using \'.
The \ can be put in a string using \\.


- - - ToHTML

Converts the content of a variable to htlm characters
Tac(
 SetVar(MyVar,Cst('<br />hello')),
 SetVar(MyHtm,ToHTML(MyVar)), // contains &lt; br &gt; hello
)



- - - ToJsAlert

Converts a string to a string that can be the content of a javaScript alert message.
The line feed (lf) is converted to the \n
The carriage return ( cr ) is converted to nothing
the \ is converted to \\
the ' is converted to \'
R_1585_Select(
 Do(
  Take(T0.Value),
  SetVar(Val,1),
  SetVar(JsFileContent,Cst(' Alert(\''),ToJsAlert(Val),Cst('\');\n'))
 )
)



- - - ToJsHtml

Converts a string to a string that can be the content of a javaScript variable.
The line feed (lf) is converted to the BR tag
The carriage return (cr) is converted to nothing
the \ is converted to \\
the ' is converted to \'
R_1585_Select(
 Do(
  Take(T0.Value),
  SetVar(Val,1),
  SetVar(JsFileContent,Cst(' var MyJsVariable=\''),ToJsHTML(Val),Cst('\';\n'))
 )
)



- - - ToHtmlEdit

Converts a string to an html edit
the carriage return is converted to nothing,
the line feed (lf) is converted to & # 10 ;,
the ' is converted to & # 39 ;,
the \ is converted to & # 92 ;,
Tac(
 SetVar(T0Value,ToHtmlEdit(T0Value))
)



- - - ToQuotes

Converts a string with
\ is converted to \\,
' is converted to \',
Tac(
 SetVar(T0Value,ToQuotes(T0Value))
)



- - - FunctionEntities

Converts a string to a Constant.
Example: in the Tool menu, the function that converts a string is:
Menu(), //Convert to function
a0_AskData(
 Par(Display(2000121),Data(6)),
 Tac(
  SetVar(Original,Input(0)),
  SetVar(Original,FunctionEntities(Original)),
  
  SetVar(out,Cst('<pre>Cst(\''),Var(Original),Cst('\')</pre>')),
  Ech1(Var(out)), 
 ) 
) 
So if in input there is ( there is a line feed at the end )

hello, 'world', with " and ( and '
in output we have this

Cst('hello, \'world\', with " and ( and \' \n')


- - Match


Does an ereg() on the last value in call stack
Syntax:
Match(xxxx)
where xxxx is the ereg string
For example, to test that a new password is correct, MyPitSelf uses the function
SetVar(NewPassword,2),
SetVar(NewPasswordConfirm,1),
Test(              // is the new password = confirmation ( passwords have to be entered twice )
 Cond(Equal(Var(NewPassword))), 
 IfFalse(ShowMsg(1000009),ReturnFalse())
),
Test(
 Cond(Match('^([a-zA-Z0-9]{6,32})$')) // contains only alpha
 IfFalse(
  ShowMsg(1000010),ReturnFalse()
 )
)



- - CutWithLastChar


CutWithLastChar(MyVar,Var(Value),Cst(',')),
Cut the value from the last position+1 founded of the third parameter ( Cst(',') here ) in value to the end and puts the result in MyVar.
Example
Tac(

 SetVar(DataValueList,Cst('a, b , c , bla bla ')),

 CutWithLastChar(DataValueList,Var(DataValueList),Cst(',')), 

 Ech1(Var(DataValueList)) // outputs "a, b , c ,"

)



- - CutWithoutLastChar


CutWithoutLastChar(MyVar,Var(Value),Cst(',')),
Cut the value from the last position founded of the third parameter ( Cst(',') here ) in value to the end and puts the result in MyVar.
Example
Tac(

 SetVar(DataValueList,Cst('a, b , c , bla bla ')),

 CutWithoutLastChar(DataValueList,Var(DataValueList),Cst(',')), 

 Ech1(Var(DataValueList)) // outputs "a, b , c "

)



- - CutString

Cut the value in call stack from a position ( starts with 0 ) for a length
Example
Tac(
 SetVar(FieldList,Cst(,toto,tata,titi))
 SetVar(FieldList,CutString(FieldList,1,1000)),  // now contains toto,tata,titi

 SetVar(FieldList,Cst(toto,tata,titi,))
 SetVar(FieldList,CutString(FieldList,0,-1)),  // now contains toto,tata,titi

)
or
 SetVar(x,VarG(xxPaWr)),
 SetVar(x,Len(x)),

 SetVar(Path,VarG(xxPtaI)),
 SetVar(Path,Cst('../'),CutString(Path,Var(x),1000)),




- - Lower or upper case

Example:
Tac(
 //==================== LowerCase ============
 SetVar(str,Cst(HeLLo)),
 SetVar(str,LowerCase(str)) // contains hello

 // or

 SetVar(str,Cst(HeLLo)),
 Var(str),
 SetVar(str,LowerCase(1)) // contains hello

 // or

 SetVar(str,LowerCase(VarG(xxxVarGvalue))) 

 //==================== UpperCase ============
 SetVar(str,Cst(HeLLo)),
 SetVar(str,UpperCase(str)) // contains HELLO

 // or

 SetVar(str,Cst(HeLLo)),
 Var(str),
 SetVar(str,UpperCase(1)) // contains HELLO

 // or

 SetVar(str,UpperCase(VarG(xxxVarGvalue))) 
)



- - ReplaceString

ReplaceString: replace a string with an other in a string
Example:
Tac(
 Cst(:),
 Chr(32),
 Var(FileName),
 ReplaceString(),          // replace ":" with " " in last value in stack
 SetVar(FileName,1)
)



- - RepPreg

RepPreg: replace a string with an other in a string using the preg specification
Example:
Tac(
 SetVar(Content,Cst('MyFunct(hello)')), 
 SetVar(Replace,Cst('/MyFunct\\((\\w*)\\)/')),
 SetVar(With,Cst('<a href="javascript:popup(\'\\\\1\')"><img src=../sc.gif border="0" alt="" ></a>')),

 RepPreg(Replace,With,Content),
)



- - StringIsInString

Return true if a string is found in an other string
Example:
Tac(
 SetVar(ToSearch,Cst('-'),Var(Parent),Cst('-')),
 Var(ToSearch),
 Var(DocList),
 Test(
  Cond(StringIsInString()),
  IfTrue(
   // do the job
  )
 )
)



- - strpos, strrpos

Returns the position of a string in an other string, strrpos starts the research from the end of the string
 SetVar(ToSearch,Cst('/')),
 Var(ToSearch),
 Var(T0NewValue),
 Test(
  Cond(StringIsInString()),
  IfTrue(
   SetVar(pos,strpos(T0NewValue,ToSearch)) // pos contains the position of the string to search.
  )
 )



- - ToArray

Tac(
 // ElementName contains '123-456
 ToArray(RuleType,ElementName,Cst(-)),  // RuleType contains now Array('123','456')
)



- - Len

Returns the lenght of the string
Example: To show the database schema:
 SetVar(x,VarG(xxPaWr)),
 SetVar(x,Len(x)),

 SetVar(Path,VarG(xxPtaI)),
 SetVar(Path,Cst('../'),CutString(Path,Var(x),1000)),




- - H2D

Converts an hexadecimal string to a decimal string
Tac(
 SetVar(MyVar,Cst('FF')),
 SetVar(MyVar,H2D(MyVar)), // Now, MyVar is 255
)



- - BranchHeader()

Creates a head string for the trees.
Tac(
 SetVar(Level,Cst(5)), // level can be 0
 SetVar(out,BranchHeader(Level))
 Ech1(Var()), outputs '<tr class=""><td><img width={level}"> '
}



- Html

Some various JS/Html functions.


- - span


- - - span

Puts a value in a span
Tac(
 SetVar(ToSearch,Cst('-'),Var(DocDisp),Cst('-')),
 Var(ToSearch),
 Var(DocHiLight),
 Test(
  Cond(StringIsInString()),
  IfTrue(
   SetVar(Name,span(ECHO,Name)) // $Name='<span class="zz_echo">'.$Name.'</span>';
  )
 ),
)
or declare a span with an ID
 SetVar(Name,SpanId(MYSPAN,ECHO)) // $Name='<span id="MYSPAN" class="zz_echo"></span>';
or declare a span with an ID as variable with or without a class
SetVar(Ms,Cst('createFields_'),Var(TableCode)),
SetVar(out,SpanVar(Ms),Var(BAll),SpanEnd()),  // $Out='<span id="'.$Ms.'">';
or
SetVar(out,SpanVar(Ms,ZZHID),Var(BAll),SpanEnd()),  // $Out='<span id="'.$Ms.'" class="ZZHID">';
or declare a span with an ID and a style
SpanStyle(SvgC,display:inline;), // $Out='<span id="SvgC" style="display:inline;">';
or declare a span with a class
SpanClass(ECHO), // $Out='<span class="zz_echo">';
or declare a span START with an ID
 SetVar(Name,SpanIdSt(MYSPAN,ECHO)) // $Name='<span id="MYSPAN" class="zz_echo">';
// or
 SetVar(Name,SpanIdSt(MYSPAN)) // $Name='<span id="MYSPAN">';
or declare a span End
 SetVar(Name,SpanEnd()) // $Name='</span>';



- - - HiddenSpan

Put the content of stack in a hidden span.
Setvar(aVariable,HiddenSpan(SPANNAME,i)),
i is the stack position of the element to put in the span
example:
Tac(
 SetVar(Button,Funct(6202)),
 Cst(0),
 Cst(.),
 Cst(p.gif), 
 Display(2000079),
 Display(2000079),
 Var(Button),
 Funct(1001),
 SetVar(Button,HiddenSpan(BOT1,1)),
 Ech1(Var(Button)),
 CopySpan(BOT1,AFTERFUNCTIONLIST), // or AFTERLISTTITLE for example
)



- - - CopySpan

Copy the content of a span to an other.
Tac(
 SetVar(Button,Cst('<span id="BOT1" style="display:none">'),1,Cst('</span>')),
 Ech1(Var(Button)),
 CopySpan(BOT1,AFTERLISTTITLE),
)
Or
Tac(
 SetVar(Spa01,Cst(BOT1)),
 SetVar(Spa02,Cst(AFTERLISTTITLE)),
 SetVar(Button,Cst('<span id="BOT1" style="display:none">'),1,Cst('</span>')),
 Ech1(Var(Button)),
 CopySpan(Var(Spa01),Var(Spa02)), // copy Spa01 in Spa02
)



- - - ClearSpan

Puts nothing in a span
 ClearSpan(MYSPAN), //document.getElementById("MYSPAN").innerHTML="";



- - - HideSpan

Hide the content of a span
Tac(
 SetVar(SpNa,Cst('SPLO'),Var(Id)),
 HideSpan(SpNa),
 // echo '<script>document.getElementById("'.$SpNa.'").style.display="none";</script>';

 HideParentSpan(SpNa),
 // echo '<script>this.parent.document.getElementById("'.$SpNa.'").style.display="none";</script>';

)



- - div

Contenu pour le chapitre div


- - - div

Declare a div with an ID and a style
DivStyle(SvgC,display:inline;), // $Out='<div id="SvgC" style="display:inline;">';


or declare a div End
 SetVar(Name,DivEnd()) // $Name='</div>';



- - table

Contenu pour le chapitre table


- - - table0

use as : SetVar(temp,table0()), Ech1(var(temp)), equivalent to
<table border="0" cellpadding="0" cellspacing="0" summary="">



- - - table1

use as : SetVar(temp,table0()), Ech1(var(temp)), equivalent to
<table border="0" cellpadding="0" cellspacing="0" summary="" align="left">



- - - tr0

use as : SetVar(temp,tr0()), Ech1(var(temp)), equivalent to
<tr>



- - - td0

use as : SetVar(temp,td0()), Ech1(var(temp)), equivalent to
<td>



- - - tdnowrap0

use as : SetVar(temp,tdnowrap0()), Ech1(var(temp)), equivalent to
<td class="nowrap0">



- JsTree

This set of function does a tree management

They may seem a little complex but in fact they are generated by a set of functions that takes a table that is self-referenced.

See the use of this tree generation in the How To's


- - JsTreeInit

This function initialises a tree:
The tipical use of this function is:

Tac(
 SetVar(out,Cst('<table><tr><td class="zz_record">')),
 Ech(Var(out)), // the tree is embeded into a table

 JsTreeInit(d1,treeCurr,treeInit,Funct(0001),Funct(0002)),
 // d1 is the javascript tree object
 // funct 0001 is the function to activate a branch
 // treeCurr is the reference to the current displayed Id
 // treeInit is the reference to the root id of the tree
 // funct 0002 is the function to manage the fold/unfold + and - buttons

//========= <branches
 Cst(d1),        // name of the JsTree object
 Var(treeInit),
 Var(treeInit),
 Var(treeCurr),
 Var(treeList),
 Cst(0),
 Funct(0003)      // function to display a branch
//========= branches>

 JsTreeOutput(d1,treeList), // outputs the tree

 SetVar(out,Cst('</td></tr></table>')),
 Ech(Var(out)),

)

An other function is used for the root of a function that manages the possibility to move the branches of a tree.
Tac(

 JsTreeInitMove(treeTree,treeCurr,treeInit,Funct(0004),treeToMove),

)



- - Branches

There are two functions:
- One for the root
- One for the branches

These functions output a tree branch
A typical use of them is:
Tac(
 Var(treeDisp),
 Test(
  Cond(Equal(Var(treeInit))),
  IfTrue(
   JsTreeRoot(TreeRef,treeDisp,Name,HasChild)
  ),
  IfFalse(
   JsTreeElement(TreeRef,treeDisp,Name,HasChild,Parent)
  )
 )
)



- - Boutons

This function is used to add buttons to the current selected branches
A typical use of this function is:
 JsTreeAdd(TreeRef,treeDisp,BEdit,BAjsBr,BAjApr,BDepla,BSupp),
 // BEdit,BAjsBr,BAjApr,BDepla,BSupp are the buttons to display



- javascripts

Contenu pour le chapitre javascripts


- - Update Parent Field

Updates the "field" in the parent window with a NUMERICAL value
Arg(field),
Arg(Value),
Tac(
 JsUpdateParentFieldNumber(field,Value),
)
Equivalent to
<script type="text/javascript">
 this.parent.document.getElementById(\''.$field.'\').value=numberToFormat(\''.$Value.'\');
</script>
Updates the "field" in the parent window with a TEXT value
Arg(field),
Arg(Value),
Tac(
 JsUpdateParentFieldTEXT(field,Value),
)
Equivalent to
<script type="text/javascript">
this.parent.document.getElementById(\''.$field. '\').value = 
\''.str_replace( '\'' , '\\\'' ,  str_replace(  '\\' , '\\\\',$Valeur ) ) . '\';
</script>



- Fonction SQL

Every sql request with a type = SELECT or INSERT or UPDATE or DELETE or DELETE VALUE you create will produce a function.
This function can be used in the language to do the work


The FIRST EXAMPLE section of this documentation give some examples of the use of this button.
When you use this button, all the informations about the request are put in tables so the request can be regenerated and can be analysed according to it's type and thus, it's use.

The request generated with this button are simple. No LEFT/RIGHT JOIN are possible yet.
You can add some little enhancements by using the "Complementary field" or the "And more" fields.

If you need a very complex request, you can create one, then change its type to MANUAL and then write what you want.
Be careful in that case to respect the good number of parameters in the request specification.


- - SELECT


a select request that looks like this:
Select T0.FIELD1 , T0.FIELD2,,,T0.FIELDn From MyTable T0
Produces a function named R_nnnn_Select() where nnnn is the request number.

The use of this function is
R_nnn_Select(
 Do(
  Take(T0.FIELDi,T0.FIELDj,,,), // value of the fields are pushed in the call stack
  ffff()                        // function[s] is[are] called
 )                              // values of the fields are poped
)
The field order given in the request can be anything.
The instructions in the Do block are repeated for each value found in the select.




- - DELETE, DELETE VALUE

a DELETE request that looks like this:
DELETE From MyTable where FIELD = %SQLPAR0%
Produces a function named R_nnn_Delete() where nnn is the request number.

The use of this function is
Tac(
 Var(IndiceToDelete),
 R_nnn_Delete()
)
The Call stack should contain the parameters of the Delete.


The DELETE VALUE type is reserved to delete records with the key = the unique ID of the Table.
Every table must contain a AUTO_INCREMENT field, other tables may refer to that field as a reference.
So an other function is produced: it is called R_nnn_CrossRef() and checks that no other table refer to the record which is deleted.
You can use this function independently but it is always called before a delete occurs.


The DELETE Type opposed to a DELETE VALUE is also delete BUT with no CrossRef check. Use it with care!



- - UPDATE


an UPDATE request that looks like this:
UPDATR MyTable set FIELD = %SQLPAR1% where record = %SQLPAR0%
Produces a function named R_nnn_Update() where nnn is the request number.

The use of this function is
Tac(
 Var(RecordToUpdate),
 Var(FieldToUpdate),
 R_nnn_Update()
)
The Call stack should contain the parameters of the Update.



- - INSERT

An INSERT request that looks like this:
INSERT into MyTable set FIELD = %SQLPAR1%
Produces a function named R_nnnn_Insert() where nnnn is the request number.

The use of this function is
Tac(
 Var(ValueToInsert),
 R_nnnn_Insert(1) // or R_nnnn_Insert(0) if you do not 
                  // want to want to know the last_insert_id
 SetVar(NewId,Var(R_nnnn_IOne)), // if R_nnnn_Insert(1)
)
The Call stack should contain the parameters of the Insert.

If the Insert is Correct and the parameter is 1, it puts into the variable Stack a variable named R_nnnn_IOne which contains the number of the Auto_Increment field of the new record.


- - MANUAL


an MANUAL request that looks like this:
SELECT count(*) as res from MyTable where FIELD = %SQLPAR0%
Produces a function named R_nnn_Manual() where nnn is the request number.

The use of this function is
Tac(
 Var(ValueToInsert),
 R_nnn_Manual(ffff())
)
The Call stack should contain the parameters of the request.

Be careful to give the good number of parameters in the definition of the Request


- - ExecuteSQL


Execute the SQL string contained in the Last value of the call stack


- Session et variables http

One can update session predefined variables ( SessionVar function)
or
define new session variables ( SessionValue functions )


- - SetSessionVar

SetSessionVar(xxx)
Updates a session variable with the 2nd last value contained in the call stack.

Example: when the user logs in, he is in a group but if you want to give the possibility to a user to change his group dynamically to allow some functions according to the context, use this type of function:
Tac(
 Var(NewGroupValue), SetSessionVar(xxUserGroup),
 Home()
)
Now, the user will have the menus of the group number contained in the variable NewGroupValue



- - SessionValue

If you need a session variable, you can define one and update or get it's value using these functions.
Example: you can put in the loggin function (4003) this code.
Tac(
 CreateSessionValue(TheNewSessionValue), // define the session value, now, 
                                         // xxxSessionTheNewSessionValue exists in the VarStack
 Var(TheValue),
 SetSessionValue(TheNewSessionValue) // puts TheValue in xxxSessionTheNewSessionValue
)
Then, anywhere in the program, you can use the code:
Tac(
 Cst(),  // put a space in the call stack
 GetSessionValue(TheNewSessionValue), // puts the session value in the call stack ( replaces the space)
 SetVar(TheValue,1) // puts the value in a variable.
)



- - HttpPostVar

Gets a POST variable
Example: This example is the source to get the user name when the user logs in
Tac(
 SetVar(UserLogginName,HttpPostVar(XXNAME)),
 SetVar(UserLogginPassword,HttpPostVar(XXPASS)),
)



- - HttpGetVar

Gets a GET variable, Example:
Tac(
 SetVar(MyVar,HttpGetVar(GETNAME)),
)



- - SetHttpPost

Sets a Post variable
Tac(
 VarG(xxAct),
 Test(
  Cond(NEqual(GO)),
  IfTrue(
   Tac(
    Cst(0),    SetHttpPost(INPUT2),
    Cst(0),    SetHttpPost(INPUT3)
   )
  )
 )
)



- - Server variables

Gets the $_SERVER variable
Example:
Tac(
 SetVar(where,Env('SERVER_SOFTWARE')),
)
The values can be for example:
HTTP_REFERER
DOCUMENT_ROOT
PHP_SELF
SERVER_NAME
HTTP_USER_AGENT



- - IsSetSessionVar

Check that a session value exist. Example
Tac(
 Cst(xxxSessionTargetIsOutside),
 Test(
  Cond(IsSetSessionVar()),
  IfTrue(
   // do something
  )
 )
)



- - IsSetPostVar

Check that a $_POST value exist.
example:
Tac(
 Cst(INPUT),
 Test(
  Cond(IsSetPostVar()),
  IfTrue(
   // do something
  )
 )
)



- - IsSetGetVar

Check that a $_GET value exist.
example:
Tac(
 Cst(s),
 Test(
  Cond(IsSetGetVar()),
  IfTrue(
   // do something
  )
 )
)



- Cookies


- - DefineCookie

DefineCookie(Cst(x)|Var(x),Cst(y)|Var(y)),
This will create a cookie


- - SetCookie

This will set a cookie:
SetCookie(Cst(x)|Var(x),Cst(y)|Var(y))


- - Get a cookie

There are 2 ways to gat a cookie:
First way: put it in the stack:
Tac(
 Cookie(Cst(MyCookieName)), // puts the cookie value of MyCookieName in the stack
 Test(
  Cond(Greater(Var(Total))),
  IfTrue(
   SetCookie(Cst(MyCookie),Cst(0))
  )
 )
)
Second way: put it in a variable
Tac(
 SetVar(MyCookieValue,Cookie(Cst(MyCookieName))),
 Var(MyCookieValue),
 Test(
  Cond(Greater(Var(Total))),
  IfTrue(
   SetCookie(Cst(MyCookie),Cst(0))
  )
 )
)



- Date et heure

These functions are set to work with date and time.
Note: I suggest that one creates a table with a calandar, fill it once every five or ten years and work on these values instead of going on computing always the same things.


- - maintenant

Tac( SetVar(now,now()) // $now='2008-09-14 12:49:35'; )


- - Date

Convert a system format date (yyyy-mm-dd) to a date according to the system setup
Example:
Tac(
 // these functions use javascript to display the date
 SetVar(MyDate,Cst(2003-10-05)),
 SetVar(MyDate,DateUser(MyDate,0)), // Contains 05/10/03 if we are in france ( DMY format )
 SetVar(MyDate,DateUser(MyDate,1)), // Contains Lu 05/10/03 if we are in france ( DMY format )
 SetVar(MyDate,DateUser(MyDate,2)), // Contains Lundi 05/10/03 if we are in france ( DMY format )
 SetVar(MyDate,DateUser(MyDate,3)), // Contains Lundi 05 Octobre 2003 

 // If you want to use php to convert the date, use DateServer instead of DateUser
)



- - a0_AddOneDay:

Computes the day after the one in the call stack. The date must be in a system format (yyyy-mm-dd)
Example:
Tac(
 Cst(2003-12-31),
 a0_AddOneDay(),
 out(1) // outputs 2004-01-01
)



- - a0_DayOfWeek

Gives the day yhe week of the dates placed in the call stack:
0:sunday
1:monday
2:tuesday
3:wednesday
4:thursday
5:friday
6:saturday
Example:
Tac(
 Cst(2003-12-31),
 FormatDate(),
 out(1),
 UnformatDate(),
 out(1),
 a0_AddOneDay(), 
 out(1),
 a0_DayOfWeek(),
 out(1), // outputs 4
)



- - SetTimeLimit


add n seconds to the execution of an interactive function. A SetTimeLimit(0) (no limit) is always called before a batch function.
Syntax
setTimeLimit(10) // adds 10 seconds to the php time limit, see the php doc for this.


- Rétro-analyse

Functions for reverse engineering
you must include the function
a0_useRev_0()
before using them


- - Rev_SourceToArray

Converts a text function to an array
Tac(
 a0_useRev_0(),
 SetVar(TheSource,
  Cst('Tac(Cst(30),R_1203_Select(Do(Take(T0.Nom),out(1))))')
 ),
 Rev_SourceToArray(FunctionSource,tf),
)
Now, tf is an array containing :
Ind Function
Number
Parent
Number
Order
in Parent
Function
Name
Function
Type
Number of
Children
Level C1 C2
0 0 -1 0 0 INIT 1 0
1 1 0 1 Tac formula 2 1
2 2 1 1 Cst formula 1 2
3 3 2 1 30 Value 0 3
4 4 1 2 R_1203_Select formula 1 2
5 5 4 1 Do formula 2 3
6 6 5 1 Take formula 1 4
7 7 6 1 T0.Nom zone 0 5
8 8 5 2 out formula 1 4
9 9 8 1 1 Value 0 5



- - Rev_ArrayToSource

Converts an array function to a source function
Tac(
 a0_useRev_0(),

 Rev_ArrayToSource(tf,FunctionSource),
 // now, Function source contains
 // Cst('Tac(Cst(30),R_1203_Select(Do(Take(T0.Nom),out(1))))

)



- - Rev_ArrayToTable

Load an array in the table yArrayFunct
Tac(

 a0_useRev_0(),

 Rev_SourceToArray(FunctionSource,tf),
 Rev_ArrayToTable(tf), // Load an array in the table yArrayFunct

 // Do big work on table yArrayFunct

 Rev_TableToArray(tf),
 Rev_ArrayToSource(tf,newSource),
)




- - Rev_TableToArray

Load the content of table yArrayFunct to an array
Tac(

 a0_useRev_0(),

 Rev_SourceToArray(FunctionSource,tf),
 Rev_ArrayToTable(tf), 

 // Do big work on table yArrayFunct

 Rev_TableToArray(tf), // Load the content of table yArrayFunct to an array

 Rev_ArrayToSource(tf,newSource),
)




- - Rev_TakeOffFunct

Rev_TakeOffFunct() takes off functions in a source
Tac(
 Cst(a0_AskData),
 Cst(a0_list),
 Cst(a0_Screen),
 Cst(3),         // function count to take off
 Rev_TakeOffFunct(FormuleFinale),
)



- Fonctions prédéfinies

These functions are considered as "system" functions.


- - Boutons

Comme on doit souvent créer des boutons, des fonctions prennent ceci en charge.


- - - Un bouton standard

Two functions, 1001 and 1002, can be used to build a button that appear on the screen.
1001 builds a button without a javascript confirmation
1002 builds a button with a javascript confirmation

An example of the use of these functions is given above:
Tac(
 SetVar(Button,Funct(xxxx)),  // put the function to call (xxxx) with this button in a variable
 Var(Value01),        // argument 1 for the function xxxx
 Var(Value02),        // argument 2 for the function xxxx
 Var(Value03),        // argument 3 for the function xxxx
 Cst(3),              // argument count
 Cst(.),              // CLASS ( . = normal link )
 Cst(.),              // Image . if none or the name of the image in the /img directory
 Display(2000018),    // TITLE (Tool tip of the button)
 Display(2000019),    // text if no images
 Var(Button),         // Function number to call
 Funct(1001),         // or 1002 for confirm
 SetVar(Button,1),    // Put the html code in a variable to display it
 Ech(Var(Button))      // displays the button
)
The result is a button that executes the function
Tac( 
 Cst(1), // content of Var(Value01)
 Cst(2), // content of Var(Value02)
 Cst(3), // content of Var(Value03)
 Funct(xxxx)
) 
1, 2 and 3 were contained in Valus01, Valus02, Valus03.


- - - Ouvrir une sous fenêtre

Functions, 1003 builds a button that opens an other window.
An example of the use of this function is given above:
Tac(
 SetVar(BMajTitre,Funct(6783)), // function to call
 Var(DocDict),                  // argument one
 Cst(1),                        // 1 argument 
 Cst(OPTION),                   // CLASS of the button
 Cst(u.gif),                    // Image
 Display(2000308),              // TITLE (Tool tip of the button)
 Display(2000308),              // text displayed if no image
 Cst(0),                        // x top
 Cst(0),                        // y left
 Cst(0),                        // x width
 Cst(0),                        // y height
 Var(BMajTitre),                // funct number to call
 Funct(1003),                   
 SetVar(BMajTitre,1),           // BMajTitre contains the code
), 
The result is a button that opens an other window and executes the function
Tac(
 Cst(123456), // content of Var(DocDict) 
 Funct(6783) 
)

Function 1004 does the same but the list parameters are saved before
Function 1016 does the same but the subwindow is hidden


- - - Sur fenêtre principale

This function is used when you want to submit the main window with a function.
As the main window is submitted, the sub window is closed.
Tac(
 SetVar(ToCall,Cst('Funct('),Funct(5001),Cst(')')),
 Var(ToCall),
 Funct(1009)
)



- - Lock and unlock a record

To unlock manually a user record, you can use function 1006 with the paramaters:
TableCode
FieldCode


- - Modifier le contenu HTML

Set the HTML content of an element with an ID (innerHTML) to a value.
example:
Tac(
 SetVar(SpanValue,Cst('hello')),
 SetVar(Sid,Cst('MySpanId')),
 Var(SpanValue),
 Var(Sid),
 Funct(1015) // updates the content of a span
)



- Divers





- - CallFormulaInStack

Executes the function in stack and submit
Example: to create a new request, one choose a table to get the table code and then the program ac_yTable_CreateSelectRequest is called. To do this, we use
SetVar(Formula,
 Cst('Tac(Cst('),
 Var(TableCode),
 Cst('),ac_yTable_CreateSelectRequest())')
),
Tac(
 Var(Formula),
 CallFormulaInStack() // The function is submited
)



- - ExecuteFormulaInStack

Executes a function and continues the execution without submitting.
It is a kind of an "Eval" function.
Example, In the little CRM, in the sub window that contains a list of the other user that have a contact in their list, I want to keep track of the list filter the user enters ( criterias ). But as it is in "only" a sub window, I don't want to keep track of this in a session variable or any other method.
So in the first list I Build this string :
SetVar(Hidden,
 Cst('SetVar(IdContact,Cst('),Var(IdContact),Cst(')),'),
 Cst('SetVar(Critere,Cst(\'\'))'),
),
And in the line function, I use
Tac(
 VarG(xxHidd),
 ExecuteFormulaInStack(),
 SetVar(IdContact,VarG(IdContact))
)



- - TestFormula

Return true if the MyPitSelf function looks OK.
Example: when one creates or update a function source, the test which is done is:
Tac(
 VarG(T0Source), // See if the function is good first
 Test(
  Cond(TestFormula()),
  IfFalse(ReturnFalse())
 )
)



- - PrintFormulaInStack


Print the formula in a table view


- - CryptValue


md5 of the last value in call stack


- - a0_Batch

Executes the function in a batch mode
Syntax:
Tac(
 a0_Batch(Funct(xxxx)[,Log(0|1)]), // or
 a0_Batch(a0_yyyy([a0_zzzz])[,Log(0|1)])
)
where xxxx is the function number to execute
yyyy is a core function
zzzz is a core librairy ( type a0_useXml ) containing yyyy

Log(0) doesn't outputs a log file that can be seen with the tool/see batch result

If the debug mode is on, Log is always set to 1 by a0_Batch
Default is Log(1)


- - IncludePhp

Example
IncludePhp(xxx)
xxx is a variable name


- - OutputBuffering

If wou want to save the html output in a variable
Example:
OutputBuffering(                // start buffering 
 Tac(                           // do the job
  Var(RFileContent),            
  PhpEvalNoReturn(),            // I want to save the result of this action
  OutputBufferingGetContents(), // I call this, the VarStack now contains the output
  SetVar(WFileContent,1)        // and I save it in a variable
  a0_File(
   FileName(CSSFile),
   FilePtr(fpw),
   Action(w),
   Do(
    a0_WriteVarStackInFile(WFileContent,fpw)
   )
  )
 )
)



- - PhpTest

Test if a php source is correct. Example:
Tac(
 Var(FileContent),
 PhpTest(),
 Test(
  Cond(EEqual(FALSE)), // test this php function
  IfTrue(
   ShowMsg(2000252),
   ReturnFalse()
  )
 )
)



- Exemples de sources


- - HTML to tidy Html

 // replace in ySource html by tidy html
 a0_useHtml_0()
 Cst(0),
 R_1892_Select(
  Do(
   Take(T0.Code,T0.Type,T0.Name,T0.Source),
   SetVar(T0Code,4),
   SetVar(T0Type,3),
   SetVar(T0Name,2),
   SetVar(T0Source,1),
   Tac(
    Var(T0Code),
    Var(T0Type),
    Test(
     Cond(Equal(DOC)),
     IfTrue(
      Tac(
       SetVar(IsHt,Cst(0))
       SetVar(ToSearch,Cst('.htm')), Var(ToSearch), Var(T0Name),
       Test(Cond(StringIsInString()),IfTrue(SetVar(IsHt,Cst(1))))

       SetVar(ToSearch,Cst('.html')), Var(ToSearch), Var(T0Name),
       Test(Cond(StringIsInString()),IfTrue(SetVar(IsHt,Cst(1))))

       Var(IsHt),
       Test(
        Cond(Equal(2)), // change this to 1 to do the job
        IfTrue(
         Tac(
          SetVar(T0Source,html_ToXhtml(T0Source)),
          SetVar(T0Source,html_getBodyContent(T0Source)),
          Var(T0Code),
          Var(T0Source),
          R_1698_Update()
         )
        ),
       ),
       Var(T0Code),
       Test(
        Cond(Equal(12)), // do it for one source
        IfTrue(
         Tac(
          Var(T0Code),
          Var(T0Source),
//          R_1698_Update()
          Ech1(Var(T0Source)),out()
         )
        ),
       ),

      )
     ),
    ),
   )
  )
 ), // 1892 



Comment faire


- Définir les css

A screen copy of this
tool can be seen here.
As the css definition was a real nightmare, a tool to produce the css files is avaible in the menu Tools/Set up css.

The tool can be used only with firefox for the moment and produces css files, they are compatible also with IE.

See an example of the result with the Maquillage button


- Créer des arbres

Suppose you have two table that looks like this ( The CRM example uses it ).

In this example, the table MyTree is seld-referenced so it is a tree.

Furthermore, the user table is using it for reference.

A function to generate the source for managing this tree can be used. See this .

The set of functions to manage this tree can bee seen here .

Then you can create the first record of the tree doing this:

Finally, you can use this tree. See the example here:

Now, we are going to generate the function so that when we create/update a user, we will be able to assign him/her a branch of a tree.

A function to generate the source for managing this can be used. See this .

The set of functions to manage this tree can bee seen here .

Then you have to update one of the functions generated to match the field name and tell the screen to update a user to use this function to fetch the tree reference like this : .

Finally, you can use this tree. See the example here:


- Raz du mot de passe de super

Le mot de passe en md5 de l'utilisateur ayant comme login/mt de passe=1/1 est:
6512bd43d9caa6e02c990b0a82652dca
Vous pouvez utiliser la requête suivante pour le mettre à jour.
update yUser SET Name='1', PassWord='6512bd43d9caa6e02c990b0a82652dca' WHERE Code=1



- Enregistrer les écrans

I think the best way to make a documentation of a program is to give screen copies of the actions to do.

If you want to do this, use the menu "profile/Record the next actions for documentation".
When this mode is set, a screen copy of each screen will be output in an html file.

You can see the result of this in the example chapter.


- Générer la doc

Perform the actions described here


- En savoir plus

Allez sur http://fr.wikipedia.org/ pour en savoir plus sur la rétro analyse.




- Convertir une base access

As I do not like ODBC, I prefered do this convertion program in VB and work directly in MySql
First, check that you have something like this in the visual basic project library list ( tools/references )

Then create a new module containing the code under and read carefully the first 10 lines of comments
Option Compare Database
Option Explicit
'====================================================================================
' This VB procedure extract from an Access database to a mysql format :
' the table structure and puts it in a file named c:\temp\stru.sql,
' the table values    and puts it in a file named c:\temp\data.sql,
'================================== It can be easily modified to match your criterias
'====================================================================================
'====================================================================================
'===================== UPDATE THIS FOR your local window setting ====================
Const Mydecimalseparator = "," ' france,italy,spain:"," , US,GB:"." ,
'====================================================================================
'Create a directory name c:\temp\
'Insert this in a vb module, place your cursor inside the extract function, and press
' the F5 key (run) on your keyboard
' If a case is not provided, the output contains an XX string so you can find
' problems looking for the "XX" string
'====================================================================================
Type typfld
 nam As String
 typ As Integer
End Type

'====================================================================================
Sub Extract()
 Dim tdf  As DAO.TableDef
 Dim fld  As DAO.Field
 Dim i    As Integer
 Dim j    As Integer
 Dim typ  As String
 Dim dba  As Database
 Dim rst0 As Recordset
 Dim shea As String
 Dim sdat As String
 Dim ttypfld(0 To 499) As typfld
 Dim val  As Variant
 Dim fou  As Boolean
 Dim dattim As Date
 Dim auto As String
 Dim dbl  As Double
 Open "c:\temp\stru.sql" For Output Access Write As #1
 Open "c:\temp\data.sql" For Output Access Write As #2
 For Each tdf In CurrentDb.TableDefs
  If Mid(tdf.Name, 1, 4) = "MSys" Then ' do not take MSACCESS system tables
  Else
   Print #1, "CREATE TABLE `" & MyNam(tdf.Name) & "` ("
   i = 0
   auto = ""
   For Each fld In tdf.Fields
    ttypfld(i).nam = fld.Name
    ttypfld(i).typ = fld.Type
    typ = ""
    If fld.Type = 1 Then typ = "ENUM('0','1')"
    If fld.Type = 3 Then typ = "INTEGER"
    If fld.Type = 4 Then typ = "BIGINT"
    If fld.Type = 5 Then typ = "DECIMAL(17,2)"
    If fld.Type = 6 Then typ = "FLOAT"
    If fld.Type = 7 Then typ = "DOUBLE"
    If fld.Type = 8 Then typ = "datetime"
    If fld.Type = 10 Then typ = "varchar(" & fld.Size & ")"
    If fld.Type = 11 Then typ = "BLOB"
    If fld.Type = 12 Then typ = "TEXT"
    If typ = "" Then typ = "XX " & fld.Type & " XX"
    
    If fld.Required Then
     typ = typ & " NOT NULL"
    End If
    
    If fld.Attributes = 17 Then
     typ = typ & " AUTO_INCREMENT NOT NULL"
     auto = MyNam(fld.Name)
    ElseIf fld.Attributes = 1 Or fld.Attributes = 2 Then
     typ = typ ' I don't know
    ElseIf fld.Attributes = 32770 Then
     typ = typ & "" ' hypertext link
    Else
     typ = typ & " XXX" & fld.Attributes & "XXX "
    End If
        
    If fld.AllowZeroLength Then
     typ = typ & " fld.AllowZeroLength"
    End If
        
    Print #1, " `" & MyNam(fld.Name) & "` " & typ & ","
    i = i + 1
   Next
   If auto <> "" Then Print #1, "  PRIMARY KEY  (`" & auto & "`),"
   Print #1, ");"
   Print #1, ""
   shea = "INSERT INTO " & MyNam(tdf.Name) & " VALUES("
   Set dba = CurrentDb()
   Set rst0 = dba.OpenRecordset(" SELECT * FROM [" & tdf.Name & "] ", dbOpenDynaset)
   Do Until rst0.EOF
    sdat = ""
    For j = 0 To i - 1
     val = rst0(ttypfld(j).nam)
     fou = False
     If ttypfld(j).typ = 1 Then
      sdat = sdat & "'"
      If val Then
       sdat = sdat & "1',"
      Else
       sdat = sdat & "0',"
      End If
      fou = True
     End If
     
     If ttypfld(j).typ = 3 Or ttypfld(j).typ = 4 Or ttypfld(j).typ = 5 Or ttypfld(j).typ = 6 Or ttypfld(j).typ = 7 Then
      If IsNull(val) Then
       sdat = sdat & "NULL,"
      Else
       dbl = val
       sdat = sdat & Replace(CStr(dbl), Mydecimalseparator, ".") & ","
      End If
      fou = True
     End If
     
     If ttypfld(j).typ = 10 Or ttypfld(j).typ = 12 Then
      If IsNull(val) Then
       sdat = sdat & "NULL,"
      Else
       sdat = sdat & "'" & Replace(Replace(Replace(val, "\", "\\"), "'", "\'"), vbCrLf, "\r\n") & "',"
      End If
      fou = True
     End If
    
     If ttypfld(j).typ = 8 Then
      If IsNull(val) Then
       sdat = sdat & "NULL,"
      Else
       dattim = val
       sdat = sdat & "'" & Format(dattim, "yyyy") & "-" & Format(dattim, "mm") & "-" & Format(dattim, "dd") & " " & Format(dattim, "hh:mm:ss") & "',"
      End If
      fou = True
     End If
     
     If ttypfld(j).typ = 11 Then
      If IsNull(val) Then
       sdat = sdat & "NULL,"
      Else
       sdat = sdat & "'',"
      End If
      fou = True
     End If
     
     If fou Then
     Else
      sdat = sdat & " XXX " & val & " XXX "
     End If
    Next
    sdat = Mid(sdat, 1, Len(sdat) - 1) & ");"
    If fou Then
     Print #2, shea & sdat
    Else
     Print #2, "#" & shea & sdat
    End If
    rst0.MoveNext
   Loop
  End If
  Print #2, ""
 Next
 Close #1
 Close #2
End Sub
'====================================================================================
Function MyNam(nam) As String ' Converts a fancy table name like "Détails commandes"
                              ' to "details_commandes"
 Dim i As Integer
 MyNam = ""
 Dim c As String
 For i = 1 To Len(nam)
  c = Mid(nam, i, 1)
  If (Asc(c) >= Asc("a") And Asc(c) <= Asc("z")) Or (Asc(c) >= Asc("A") And Asc(c) <= Asc("Z")) Or (Asc(c) >= Asc("0") And Asc(c) <= Asc("9")) Then
   MyNam = MyNam & LCase(c)
  ElseIf c = "é" Or c = "è" Then ' ( this is for france, update it for other languages )
   MyNam = MyNam & "e"
  ElseIf c = "à" Then
   MyNam = MyNam & "a"
  Else
   MyNam = MyNam & "_"
  End If
 Next
End Function
'====================================================================================



- Une page

Pour voir cette documentation en une page, cliquez ici.

Pour voir cette documentation en multipage, cliquez ici.




- Utiliser google maps

Pour jouer avec Google maps, Cliquez ici.




- SQL JOIN

This documentation helps me sometimes ( I do NOT like SQL Join but I must admit that sometimes, it helps ! )
DROP TABLE IF EXISTS `Group`;
CREATE TABLE `Group` (
`IdGroup` BIGINT( 20 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
UNIQUE (`IdGroup` )
);

INSERT INTO `Group` VALUES (1),(2),(3),(4),(5);

DROP TABLE IF EXISTS `Auth`;
CREATE TABLE `Auth` (
  `groupId` BIGINT(20) UNSIGNED NOT NULL default '0',
  `menuId`  BIGINT(20) UNSIGNED NOT NULL default '0'
);


INSERT INTO `Auth` VALUES (0, 1);
INSERT INTO `Auth` VALUES (2, 1) , (2, 2) , (2, 3) , (2, 4) , (2, 5) ;
INSERT INTO `Auth` VALUES (3, 1) , (3, 2) , (3, 3) , (3, 4) ,         (3, 9) ;

# tous les groupes et leurs autorisations ( NULL si aucune autorisation pour un utilisateur )
SELECT  T1.IdGroup, T0.menuId 
FROM `Auth` T0 
RIGHT OUTER JOIN `Group` T1 ON ( T0.groupId = T1.IdGroup ) ;
#returns: 1,NULL| 2,1 | 2,2 | 2,3 | 2,4 | 2,5 | 3,1 | 3,2 | 3,3 | 3,4 | 3,9 | 4,NULL | 5,NULL


# tous les groupes qui n'ont pas d'autorisations ( sql précédent plus recherche de NULL )
SELECT T1.IdGroup, T0.menuId  
FROM `Auth` T0 
RIGHT OUTER JOIN `Group` T1 ON ( T0.groupId = T1.IdGroup ) 
WHERE T0.menuId IS NULL ;
#returns: 1,NULL | 4,NULL | 5,NULL

# tous les groupes avec autorisations
SELECT  T1.IdGroup, T0.menuId
FROM `Auth` T0 
INNER JOIN `Group` T1 ON (T0.groupId=T1.IdGroup);
#returns: 2,1 | 2,2 | 2,3 | 2,4 | 2,5 | 3,1 | 3,2 | 3,3 | 3,4 | 3,9

# Groupes dans la table des autorisations qui ne sont pas référencés dans la table des groupes
SELECT T0.groupId , T0.menuId 
FROM Auth T0 
LEFT JOIN `Group` T1 ON T0.groupId=T1.IdGroup  
WHERE T1.IdGroup IS NULL;
#returns: 0,1




- fckEditor configuration

The fck editor may be used and if you define a data as HTML type and you give the name mypsFckHtml1 to this type, then you must add this definition to the file fckconfig.js.
FCKConfig.ToolbarSets["mypsFckHtml1"] = [
	['Source','NewPage','Cut','Copy','Paste','PasteText','PasteWord'],
	['Undo','Redo','Find','Replace','SelectAll','RemoveFormat','JustifyLeft','JustifyCenter','JustifyRight','JustifyFull'],
        ['OrderedList','UnorderedList'],
	'/',
	['Bold','Italic','Underline','StrikeThrough','Subscript','Superscript','FontSize','TextColor','BGColor'],
	['Outdent','Indent','Blockquote'],
	['Table','Rule','SpecialChar','PageBreak','ShowBlocks','About']
] ;



- Pangramme

Ceci est un nouveau texte créé dans « word » sans mise en forme initial. On ajoute des retours à la ligne comme après le point suivant. Maintenant, on ajoute des caractères GRAS ou italiques ou soulignés, voir même, les trois à la fois.

Avec un double saut de ligne et un saut de ligne forcé en restant dans le même paragraphe ( shift entrée ).

Portez ce vieux whisky au juge blond qui fume sur son île intérieure, à côté de l'alcôve ovoïde, où les bûches se consument dans l'âtre, ce qui lui permet de penser à la cænogenèse de l'être dont il est question dans la cause ambiguë entendue à Moÿ, dans un capharnaüm qui, pense-t-il, diminue çà et là la qualité de son œuvre

L'île exiguë
Où l'obèse jury mûr
Fête l'haï volapük,
Âne ex æquo au whist,
Ôtez ce vœu déçu.

Dès Noël où un zéphyr haï me vêt de glaçons würmiens, je dîne d’exquis rôtis de bœuf au kir à l’aÿ d’âge mûr & cætera ! ·
il y a une tabulation à la fin de la ligne précédente.

&é~'{}'()[]-|è`_\ç^ à@°+=¨µ*%ù§!/:.;?,<>¤£²
L’essai a-t-il été concluant ? modif 1 € , un backtick => ` “Guillemets bizarres” de « word » ‘ça passe ?′ ‘’‚‛“”„‰…•!"#$%&'()*+,-./:;<=>?[\]^_`{|}~°®©¡£¤¥¦§¨©ª «ÉÈËÊ¿¾½¼»±²³´µ·¸¹ºæ÷ αβγδεζηθικμνξοπρςστυφχψω ℅™€⅓⅔⅛⅜⅝⅞♠♣♥♦♪♫'...


- simpleXml sub tree

$string = <<<XML
<a>
 <b>
  <c>text <b>hello</b><i>hello</i></c>
  <c>stuff</c>
 </b>
 <d>
  <c>hella</c>
 </d>
</a>
XML;

function subTree( $s , $tag ){
 if(substr($s,0,1+strlen($tag))!='<'.$tag) return false;
 $news='';
 for($i=1+strlen($tag);$i<strlen($s);$i++){
  if(substr($s,$i,1) == '>') break;
 }
 $news=substr($s,$i+1);
 if( substr($news,strlen($news)-strlen($tag)-3) !='</'.$tag.'>') return false;
 return(substr($news,0,strlen($news)-strlen($tag)-3));
}

$xml = new SimpleXMLElement($string);

echo '<textarea rows="10" cols="40">' . $xml->asXML() . '</textarea><br />';
echo '<textarea rows="6" cols="40">' . $xml->b->asXML() . '</textarea><br />' ;
echo '<textarea rows="6" cols="40">' . subTree( $xml->b->asXML() , 'b' ) . '</textarea><br />' ;

?>



- Liens sympas :-)


Where the hell is matt ?
TED.COM:
Paradox of choice
Why are we happy
Why are we happy (more)
The expected value of any of our actions that is the goodness that we can count on getting is the product of
the odd that this action will allow us to gain something
and
the value of that gain to us
8 secrets of success

Bouing ! :-))

koolsol : un jeu de solitaire en progressive web app :-)

Classement selon le score de google lighthouse des pwa


Contact