Context and Objectives

In an Oracle APEX application, there is possibility to enter Help text at development stage. The help text may be used to provide page level context sensitive help.
In almost every component, there is a specific property (Help Text) aimed to receive the text.

Usually, the developer is responsible for this task and that has drawbacks :

  • The text can only be provided in the Developer interface.
  • There is no rich text editor support at this level
  • The developer himself is not, most of time, the best qualified person to handle this job

Given these above considerations:

  • We need a more confortable tool for authoring Help Text
  • It’s better to delegate this job to a writer who will have better user sensitivity.
  • the text needs to be continually improved as it is used, without doing back and forth in the development console.

Let’s consider a possible solution to achieve our goal.

In this paper, we’ll focus on Help Text at page level only and we’ll not consider the other level of component (items) neither Inline Help Text.

Global approach

One solution is to add an application table wich contains specific help text.
The authors will need a regular access to application in order to modify the help text through a dedicated form page.

In standard, the help texts are accessible through an APEX view: APEX_APPLICATION_PAGES

Access key is : APPLICATION_ID + PAGE_ID
text: HELP_TEXT

We build a new table which contains customized help texts. Let’s say : PLA_HELP

CREATE TABLE PLA_HELP
( APP_ID NUMBER,
  PAGE_ID NUMBER,
  HELP_TEXT CLOB,
  CREATED_BY VARCHAR2(50),
  CREATED_ON DATE,
  CONSTRAINT PLA_HELP_PK PRIMARY KEY (APP_ID,PAGE_ID)
  USING INDEX ENABLE
) 

We build a view PLA_HELP_V that superimposes help text derived from repository and help text coming from our specific table.
Priority is given to Customized Help Text.

CREATE OR REPLACE FORCE EDITIONABLE VIEW PLA_HELP_V (APP_ID, PAGE_ID, CREATED_BY, CREATED_ON, PAGE_TITLE, HELP_TEXT, IS_FACTORY) AS
select
  p.APPLICATION_ID,
  p.PAGE_ID,
  h.CREATED_BY,
  h.CREATED_ON,
  p.PAGE_TITLE,
  case when h.HELP_TEXT is null 
     then p.HELP_TEXT
     else h.HELP_TEXT
  end HELP_TEXT,
  case when h.HELP_TEXT is null 
     then 'Y'
     else 'N'
  end IS_FACTORY
from APEX_APPLICATION_PAGES p,
     PLA_HELP h
where p.APPLICATION_ID = h.APP_ID(+)
and p.PAGE_ID = h.PAGE_ID(+)

Displaying help text needs a specific page dedicated to that.
When we launch the wizzard, the page and navigation icons are automatically created if a page at least is added, but it’s not the case otherwise.

Help about a page. A edit icon allows to navigate to edit mode for authorized users.
Edit Help text page.

My first choice has been to create a Instead of Trigger but this way reached an obstacle: I got an -1031 error insufficient privileges, because of presence of a system APEX view. I could workaround by granting some privileges directly to user, but I gave this approach up and I put all the code in the APEX form.

In the process section, we remove the default form DML process and replace it by three processes, each dedicated to an action (Update, insert or delete)

ie: the code for update below:

BEGIN     
 update PLA_HELP
     set
       HELP_TEXT = :P35_HELP_TEXT,
       CREATED_ON = :P35_CREATED_ON,
       CREATED_BY  = :P35_CREATED_BY
     where APP_ID = :P35_APP_ID and
           PAGE_ID = :P35_PAGE_ID;
 COMMIT;
END;

Conclusion

The new system provides a more flexible solution for providing accurate information to users.

We could imagine to refine our help system by adding multiple levels of reading, depending the user profile.
This solution can be improved, as well, by merging additional infos provided later by developper (because of fixing bugs or adding features) with an already existing customized text.

Even with the last version of Oracle APEX (22.2) which embeds Version 5 of FullCalendar, displaying can be slighty improved to give a better rendering on mobile devices.

Below are two samples emulated on iphone 6: First one is a displaying with default values, Second is a a calendar with some additional actions.

In the default version, the title for Contextual dates are too larges and some buttons are not displayed. The navigation buttons are confusing.
In the customized version, the navigation button are better reachable at right side with more intuitive icons, size of contextual date is reduced and view buttons are displayed entirely.

Additional actions

In the improved version, we:

  • Hide navigation option in Calendar Views and Navigation section
    The standard arrows will be replaced by customized buttons
  • Add three buttons in EDIT section of the Calendar region
    • Previous
      • DA on button: apex.region("calendar").widget().data("fullCalendar").prev();
      • ico: fa-arrow-left-alt u-color-1-text
    • Today
      • DA on button: apex.region("calendar").widget().data("fullCalendar").today();
      • icon: fa-pie-chart-0
    • Next
      • DA on Button: apex.region("calendar").widget().data("fullCalendar").next();
      • icon: fa-arrow-right-alt u-color-1-text
  • override class style at the page level in INLINE CSS section

.fc .fc-toolbar-title {
font-size: small;
}

un article sur le Bitcoin que j’avais écrit en 2018 et qu’il faut que je réactualise …

« Mon cher Jack,

 Comme je te l’ai écrit dans ma dernière lettre, on se retrouvera début septembre à Skagway. J’ai raflé tout le stock chez le chinois de Market Street. Il était tout excité et m’a promis que cela tiendrait 14 Téra Hash mais tu ne sais jamais très bien avec lui… Et puis, j’ai aussi rassemblé suffisamment de chiens pour monter nos vivres et toutes nos cartes ASIC. Notre claim est à trois jours du Chilkoot. Il y a plein de torrents autour et de quoi dresser des barrages pour donner du jus à nos cartes. Crois-moi, elles vont gentiment ronronner et nous tenir chaud pendant le blizzard et au printemps, on sera riche, Jack ! et cette fois-ci, je te prédis que nous traverserons Dawson sans verser une goutte d’alcool, sans même aller rendre une visite à Julia, nos poches gonflées de bitcoins, comme des seigneurs sous les étoiles.

Bien à toi,

SF Juin 2018″

Et voilà, mon histoire s’arrête provisoirement ici. Comment vais-je parvenir à placer tous ces trucs de hash, de nonce, de courbes elliptiques ou de peer-to-peer ? La plume du grand Jack n’aurait pas tremblé, elle !

De la confiance

J’entendais parler du bitcoin depuis longtemps avec indifférence mais si je me rappelle bien, c’est en mai 2017, lorsque des hackers ont disséminé le virus WannaCry puis réclamé une rançon en bitcoins que, dans mon esprit, la légitimité de cette monnaie venait ironiquement d’être établie. J’entrevoyais que si le bitcoin permettait un échange de valeur entre citoyens ordinaires d’une part et escrocs de l’autre, à la vue de tous, alors cette monnaie universelle était vraiment digne de confiance !

Ce qui me frappe aujourd’hui en observant le système Bitcoin, c’est sa robustesse. Faut-il que ce cela soit bien conçu pour fonctionner sans véritable accroc depuis sa création en 2009 ! C’est la plus fameuse blockchain que l’on puisse imaginer car elle est publique et cristallise sur sa technologie 130 Milliards d’Euros, ce qui représente un sacré tas de confiance.

Le bitcoin est le nom de la monnaie qui est produite par le système Bitcoin qui est, lui, un écosystème comprenant des développeurs, du logiciel, un protocole, une communauté d’intérêt à entrée libre. Le bitcoin, contrairement à l’or,  ne possède aucune valeur intrinsèque. Il doit être considéré essentiellement comme un système de paiement. Il peut éventuellement être perçu comme valeur refuge.

De l’énergie

Toute cette belle confiance fossilisée dans le bitcoin est basée in fine sur une dépense énergétique ahurissante. Ce 16 avril, au moment ou je commence ces lignes, 11.500 sites informatiques bourrés d’ordinateurs tournant à plein régime participent à un jeu de devinette à l’échelle mondiale en consommant l’équivalent de 50 TWh/an , soit 10% de la production Française d’électricité. Et c’est sur ce mécanisme incroyable à la fois absurde et génial que fonctionne le bitcoin. Pourquoi ? En raison d’un enchaînement de conséquences liées au caractère décentralisé d’une blockchain publique et donc du Bitcoin en particulier.

On va produire de la monnaie de façon décentralisée, mais cependant maîtrisée, et confier à un grand nombre de volontaires, appelés des mineurs dans le jargon Bitcoin, le soin de garder une copie du registre de tous les virements effectués avec cette monnaie depuis sa date de démarrage en 2009. Allons-nous confier la tenue des comptes à de parfaits inconnus ? Oui, c’est exactement ce que l’on va faire sous la forme de ce qui peut s’apparenter à une franchise.

A cette fin, on élabore un Grand Livre comptable dans lequel sont enregistrés tous les mouvements de bitcoins d’une partie vers une autre, c’est à dire d’une personne privée ou morale, vers une autre. D’un point de vue technique, ce Grand Livre est matérialisé par un ensemble de fichiers organisés sous la forme d’une blockchain. C’est à l’intérieur de celui-ci que sont enregistrées toutes les transactions financières qui sont signées à l’aide de clefs asymétriques, puis regroupées en blocs qui sont tricotés ensemble à la manière d’une écharpe qui grandirait inexorablement. Toute tentative pour y introduire une opération frauduleuse qui ressemblerait à celle-ci : « je verse, ex nihilo, un million de bitcoins à moi-même, le 16 avril »  est considérée d’un point de vue cryptographique comme quasiment impossible, quand bien-même le Grand Livre est accessible à tous.

Liberté ensuite, à qui le veut, de télécharger sur son ordinateur le logiciel Bitcoin Core qui récupérera une copie de cette blockchain  puis de se déclarer comme volontaire (mineur) pour en assurer une réplication conforme à toutes les autres copies dans le monde. En échange du service qui est, concrètement, celui de maintenir un niveau de disponibilité indiscutable (en raison des milliers de répliques du Grand Livre), le système Bitcoin rétribue chaque mineur selon un barème évoluant dans le temps mais en assortissant le deal d’une autre contrepartie qui est celle de participer à un jeu coûteux en temps. Oui un jeu, une sorte de loterie !

Du consensus

L’objectif primordial, dans un système décentralisé, est d’obtenir un consensus sur un exemplaire VRAI et donc unique du Grand Livre tout en évitant un emballement du nombre de synchronisations (peer-to-peer) entre les comptables (les mineurs) pour savoir quelle est la bonne version et en évitant également la formation d’une situation monopolistique (> 51% de mineurs complices).

Venons-en à ce que je considère comme la plus grande et géniale monstruosité qui constitue un sérieux défi à l’entendement: On introduit un mécanisme de ralentissement qui vise à garder à la fois un nombre raisonnable de mineurs et un nombre raisonnable d’échanges entre eux pour ne pas saturer le réseau. C’est cette même mécanique qui permet, indirectement, de réguler la rareté de façon déterministe puisque le nombre de bitcoins à atteindre, selon les statuts de départ, est de vingt-et-un millions, pas un de plus, selon une cadence programmée jusqu’en 2040. On va donc obliger les mineurs à perdre du temps dans un jeu de devinette. Celle-ci nécessite que chaque mineur soit équipé d’un ordinateur, d’un accès au réseau internet et d’un logiciel spécialisé, le Bitcoin Core. L’émergence du consensus est ainsi acquise à l’aide du mécanisme précédent désigné par l’expression « preuve de travail  » (POW). C’est également le cas pour Ethereum qui est une autre blockchain très populaire. La question est de savoir comment objectiver cette quantité de « travail » et c’est maintenant qu’il faut rappeler les vertus du hash.

Du hash

« De toute ma vie, je n’avais jamais vu une telle quantité de hash partir en fumée! « 

Le système Bitcoin est conçu pour « fatiguer » les machines des mineurs, éprouver leur détermination pour ainsi dire. Bitcoin utilise ad nauseum la technique de hashingIl s’agit d’un procédé logiciel qui consiste à produire, à partir de n’importe quoi (un texte, des nombres, une image, un livre complet) une suite de caractères d’une longueur fixe de 256 bits qui représente un condensé unique appelé hash. Ce dernier se présente soit comme une suite de deux cent cinquante-six chiffres 1 et 0, soit comme un mélange de trente-deux chiffres et lettres ou soit comme un nombre très grand pouvant aller jusqu’à 2 puissance 256. C’est juste une affaire de représentation. SHA256 est l’une des variantes de ce procédé. C’est un beau bijou logiciel digne d’une montre à complications et qui mériterait lui aussi d’être porté au poignet. En décrire le fonctionnement sort du cadre de cet article mais je peux simplement dire que, si deux choses auxquelles on applique le hashing présentent une différence, aussi insignifiante soit-elle, alors les valeurs de hash obtenues seront totalement différentes. Par exemple, si je modifie un seul caractère du texte de la Bible, une autre personne peut le détecter immédiatement en constatant que la nouvelle valeur de hash vient de changer radicalement. C’est donc très pratique pour garantir l’inviolabilité d’une information. A contrario, je mets au défi quiconque de trouver, à partir d’une valeur de hash prise au hasard, quelle est la chose qui lui a donné « naissance ».
Cependant … il est de l’ordre du possible de lancer un challenge qui ressemblerait à celui-ci : « Trouver une chose dont la valeur de hash commence par une série de 18 zéros. »
C’est exactement ce genre de devinette qui sera soumise aux ordinateurs ! Après des milliards de milliards de « coups de hachoir », si on parvient à obtenir un hash présentant cette caractéristique, le jeu de devinette est gagné. S’il faut augmenter la difficulté, le défi deviendrait simplement : « Maintenant, encore plus fort: c’est 19 zéros et non plus 18. « .

Si on se relie à nouveau à notre Grand Livre, le principe de la devinette évoquée plus haut consiste à « tomber » par chance sur un nombre appelé nonce, (2) tel que, combiné avec le contenu d’une nouvelle page du Grand Livre contenant les enregistrements des toutes dernières transactions, celui-ci produise à l’issue d’un hashing une valeur de hash présentant des caractéristiques morphologiques précises (Target) comme illustré précédemment.

Pour donner une idée, il faut environ actuellement un trillion d’essais, environ, avant de tomber sur un bon nombre. Tout dépend si on a de la chance, mais disons que c’est calibré pour durer environ dix minutes. Le logiciel de Bitcoin communément utilisé par chacun des mineurs agit ainsi à la manière d’un pacemaker qui fixe le niveau de rareté en fonction du nombre de mineurs à un instant donné et de la puissance intrinsèque des ordinateurs qui, elle, progresse régulièrement en raison des progrès de la technologie. La difficulté augmente tous les 2016 blocs, c’est à dire toutes les deux semaines environ.

Dès qu’un mineur trouve une combinaison correcte, son logiciel avertit tous les autres (peer-to-peer). A partir de ce moment là, tous les autres mineurs sont tenus de vérifier que le nombre est effectivement correct, et s’inclinent alors en acceptant la victoire du compétiteur et ajoutant au Grand Livre la nouvelle page, c’est à dire le bloc qui vient d’être validé par le vainqueur. Résultat: 12,5 bitcoins dans la poche de celui qui a trouvé le nombre, plus une petite rémunération associée (fee) à chacune des transactions enregistrées dans le bloc, et on est reparti pour le tour suivant ! Précisons que c’est uniquement à ce stade qu’a lieu le processus de création monétaire. Au 16 avril 2018, le bitcoin (BTC) s’échange contre 6.564 € et donc le gagnant ramasse environ 80.000 €.De l’électricité est consommée (il vaut mieux être en Chine), de la chaleur produite (il vaut mieux être en Islande) , du bruit n’en parlons pas (il vaut mieux être sourd) , tout cela pour être le premier à lever le doigt. C’est complètement idiot, non ? C’est à la fois absurde et génial. C’est le prix à payer pour obtenir un consensus dans une communauté d’intérêts dénuée de confiance (trustless). Il existe d’autres méthodes sur lequel le système Bitcoin aurait pu s’appuyer pour obtenir un consensus, mais celle-ci s’avère très robuste lorsque l’environnement ressemble d’avantage au Far-West qu’à une association diocésaine.

Dans ce qui précède, j’ai eu recours à un peu d’anthropomorphisme en parlant de mineurs, mais j’aurais dû employer le terme plus exact de « logiciel Bitcoin utilisé par le mineur« .

La ruée

Inutile d’insister sur les enjeux ni de préciser qu’il existe une course aux armements en matière de puissance de calcul. Les processeurs classiques sont désormais hors jeu dans cette compétition. On détournait encore très récemment l’usage des cartes graphiques élaborées (celles utilisées par d’autres vrais gamers) pour leur faire exécuter ces fameuses opérations de hashing, mais elles sont désormais supplantées par des circuits spécialisés (ASIC) qui ne savent rien faire d’autre que du hashing.

Une carte spécialisée ASIC effectue 14 Teras, soit environ 14.000 milliards d’opérations de hashing en une seconde. Une machine de ce type vaut 2.400 €. Comme il faut concentrer beaucoup de puissance pour se garantir un niveau de rémunération qui ne soit pas trop aléatoire, beaucoup de mineurs se contentent de rejoindre des sortes de coopératives (mining pool) et le butin, s’il y en a, est partagé selon des règles propres à la coopérative. La puissance totale recensée au mois d’avril 2018 est de l’ordre de 30 millions de Tera H/s, soit 30 Peta TH/s. C’est ce qu’on appelle le Hashrate. Le logiciel Bitcoin Core tient compte de cette information pour moduler le niveau de difficulté. En avril 2018, la Chine où le charbon est abondant et par conséquent l’électricité bon marché (0.11 $ /kwh), concentre plus de 51% des pools de mineurs (AntPoolF2PoolBTCC). Ce dernier point ne signifie pas que nous sommes en présence d’une situation monopolistique mais entraîne cependant des interrogations. Le système Bitcoin n’est de toute façon qu’une interminable liste de « que se passerait-il si… ».

De la raison

le Bitcoin est donc cette expérience révolutionnaire qui se déroule sous nos yeux en temps réel et qui est passée directement du stade de white paper en 2009 à celui de laboratoire puis celui de banque alternative crédible. Le Bitcoin a popularisé l’emploi d’une approche décentralisée pour réinventer des modèles d’échange de valeurs, et affronter de nouvelles classes de besoins que notre esprit conformiste ou notre fameux bon sens nous aurait empêcher d’envisager sous une autre forme que celle passant par le « nécessaire » tiers de confiance. C’est le plus bel exemple de blockchain en activité.

 » Est-ce bien ce monde meilleur que tu souhaitais Jack ? »


(1) Les avis sur cette cryptomonnaie sont partagés, évidemment. Des économistes prétendent qu’elle alimente une bulle spéculative et fait courir des risques importants au système financier. Le prix Nobel JeanTirole ou bien Jamie Dimon (JP Morgan) ont écrit récemment des articles très négatifs sur le bitcoin. Ce dernier est un « objet » tellement révolutionnaire et échappant à toute expérience passée que même des économistes chevronnés ne parviennent pas à s’accorder pour dire, par exemple, si le bitcoin est assimilable à une pyramide de Ponzi ou non.

(2) Devant la montée en puissance des processeurs spécialisés (ASIC) en hashing, il a fallu se libérer de la valeur du nonce qui permettait de monter à « seulement » quatre milliards en lui ajoutant d’autres combinatoires. Ce fut l’objet du protocole stratum qui permet à un pool de mineurs de travailler efficacement en partageant des « plages de calcul »

Oracle APEX Display Map item allows displaying a map with a point given as a GeoJSON object.

What happens if we have only two columns Lat and Long ?

We have just to transform them as a json object through a custom function as below:

create or replace function TO_GEOJSON
(lg in VARCHAR2,
lat in VARCHAR2)
return VARCHAR2
is
begin
return '{"coordinates":['||lat||','||lg ||'],"type": "point" }';
end;

In source properties of the map item, we choose :

  • Type: Expression
  • PL/SQL expression: to_geojson(:PXX_LONGITUDE, :PXX_LATITUDE)

Le bruit métallique provenait du centre du monde, c’est-à-dire de l’Arsenal.
Dans la forge principale, des corps rougeoyants de sueur frappaient sans relâche et, parmi eux, Nino ébauchait une charnière de gouvernail pour la nef qui allait bientôt être mise à l’eau.
Le bassin de Saint-Marc était encombré de navires de tous calibres produits à la chaine. « L’arsenal dévore tout » disaient les Vénitiens. La seule figure aimable parmi cet étalage de puissance était celle de la lionne efflanquée en marbre (1), placée à l’entrée, que Nino avait pris l’habitude de tapoter avant d’aller franchir l’enceinte.

Una bella face

— Bonjour Candiano ! J’espère que ma coque fera bien mille-deux-cents botti cette fois-ci !
Un bon rire franc suivit. C’était l’entrée en matière habituelle de Tomasoni qui s’amusait probablement  à rappeler un vieux différend. L’armateur, venait régulièrement s’informer de l’avancement des travaux auprès d’Angelo Candiano, oncle de Nino et chef d’atelier à la Darsena Grande. Il était venu cette fois-là accompagné de sa fille ainée, une brune sublime aux yeux bleus, comme seule la république de Venise savait en « produire ». Tandis que l’ingénue s’avançait prudemment entre les établis, sa robe éparpillait des parfums violents de musc et de gingembre. Cette irruption de beauté dans le fatras quotidien de métal et de graisse réveilla chez Nino la petite musique qui résonnait dans sa tête depuis quelques temps, celle qui rappelait que l’heure était venue d’échapper à son destin de proti et qu’il ne fallait plus s’interdire de rejoindre la société de ceux qui pouvaient tout posséder, qui étaient dominants et décomplexés, qui posaient leur regard sans trembler sur les plus belles filles … la caste des commerçants.
Depuis la vilaine épidémie de peste qui avait frappé durement la population, chacun des survivants fixait le soleil avec reconnaissance mais Nino, lui, demandait davantage à la chance.

Finalmente un’apertura!

La bonne affaire, c’était un marin pilote qui lui avait raconté son dernier voyage sur la nouvelle ligne commerciale de Ceuta et décrit cette substance qui faisait du bien à tout le corps et qu’on produisait chez les Maures.

— C’est du chanvre ! Pas celui de nos cordages, hein, mais de la résine. Ça guérit presque tout et ils en consomment aussi pendant leurs fêtes. Je connais des Espagnols qui peuvent me fournir la marchandise que je peux charger avec mon droit de portage. Il faut apporter trois cents ducats, deux cents cinquante pour mes fournisseurs et cinquante pour moi. Ensuite, tu revends à des herboristes du Rialto ou bien des médecins des environs et tu peux faire un joli pactole de plus d’un millier de ducats ! Je te laisse réfléchir, La Contarina repart dans une semaine.

Quittant le quartier des entrepôts, Nino prit le chemin de l’Arsenal mais fut obligé de s’abriter sous le porche de Santa Maria della Pieta. Il pleut souvent en juin et l’orage plongea la cité quelques instants dans les ténèbres puis s’éloigna rapidement. Après que le calme fut revenu sur le quai pavé, un miroir d’eau singeait une figure étrange de nuages isolés dans le ciel, tandis qu’au loin, l’église San Giorgio Maggiore ruisselait de lumière. C’est comme si le soleil triomphant de Byzance avait jailli au secours de sa ville-sœur.

All’Isola dei Morti

Nino jugea qu’avant d’entreprendre quoi que ce soit il lui fallait, symboliquement, tendre une main à la mer et aux défunts. Tandis qu’il se dirigeait vers l’île des morts pour se recueillir sur la tombe de ses parents, le batelier qui le conduisait évita de peu un cygne égaré qui errait sur la lagune. L’aspect incongru de cet animal fascina Nino qui ne put s’empêcher de méditer sur ce signe que lui envoyait le destin, comme un encouragement à poursuivre son projet. La barque continua dans un clapotis familier. Des oiseaux posés sur chacune des bricoles veillaient silencieusement sur les embarcations qui glissaient dans le chenal. C’était la tâche que leur avait assignée le sénat de Venise qui imposait, en tous lieux, sa juridiction aux hommes comme à la nature.

Après avoir déposé une offrande sur la sépulture, il eut fallu, bien sûr, aller jusqu’à la Basilique Santa Maria de Torcello pour obtenir une bénédiction de qualité puis plonger ses mains dans le limon originel, la matrice de cette société lacustre qui était parvenue à rassembler la plus grande quantité de richesses en occident à partir d’un tas de boue, mais cela exigeait un long détour et il se contenta de pousser vers l’île Madonna del Monte. Ce lieu solennel était suspendu entre ciel et mer. Deux sœurs à demi-folles avaient préféré rester là après le départ de leur congrégation à Mazzorbo. Elles y prodiguaient des protections plus ou moins divines et n’étaient pas avares non plus d’indulgences de toutes sortes.

De retour sur la Fondamenta Nove, le plan d’action prit rapidement forme dans son esprit. Il s’agissait désormais de réunir le capital, ce qui dans cet empire du commerce relevait d’une grande banalité, pensa-t-il. Son ami Jacopo devait bien savoir à qui s’adresser.
Tous deux avaient grandi dans la même cour et bu l’eau tirée au même puit. Jacopo s’était toujours bien débrouillé en dessin et sa réputation de peintre grandissait puisqu’il venait d’obtenir une commande importante d’une confrérie religieuse. Nino se rendit sur son chantier à la Scola Grande di San Marco.

Perché au sommet d’un échafaudage, l’énergumène s’acharnait dans une position compliquée sur une tête d’homme nu.

Amico ! Quelle puanteur ! Tu ne t’es pas lavé depuis quand ?
— Tu crois que j’ai le temps, sciocco? Je dois livrer mon esclave dans une semaine.
— Tu t’es goinfré de commandes et tu n’arrives plus à livrer, hein ?
— Hé, mais tu t’intéresses à la peinture maintenant ? souffla Jacopo en bondissant de son tréteau.
— Ecoute, dit Nino reprenant son air sérieux, je suis sur un bon coup … de la marchandise haut de gamme. Toi qui es toujours fourré dans le Cannaregio, tu dois bien connaitre un prêteur !
— J’y fréquente plutôt des prêteuses, si tu vois mais … pour mon meilleur ami, la mémoire va bien me revenir…voyons… vas au Campo del Ghetto et demande Auser, de ma part. Il prête à huit pour cent. Tu ne trouveras pas mieux.
— Tu veux en être ? tenta Nino
— Oooh non ! Je me sens d’avantage doué pour devenir célèbre que pour devenir riche !
Voila, tu sais tout, j’y retourne, Arrivederci !

Il grimpa à nouveau sur l’échafaudage un pinceau entre les dents, tel un fauve affamé et joyeux de consommer sa proie.

— Levez-vous esclaves ! je reviens terminer vos tristes faces. Et toi, Le Titien, vieux chenu, je vais te donner la leçon !

Le jour-même, Nino alla se présenter au guichet du prêteur en se recommandant du Tintoretto, comme convenu. Le vieux juif, au bout de quelques questions serrées et ayant constaté par-dessus tout qu’il n’y avait aucun gage, l’envoya promener. De toute manière, son genre à lui c’était plutôt le sel d’Ibiza. Moins risqué. La réponse des autres préteurs que Nino alla consulter dans le quartier ou à Mestre ne fut pas différente. Quant aux banquiers Lombards du Rialto, leurs taux étaient décourageants.

è finita ?

Comme la galée partait dans trois jours mais qu’il restait toujours une opportunité de fret pour le retour, Nino se décida, malgré lui, à mettre son oncle dans la confidence et lui expliqua les grandes lignes de son idée.

— Regarde les choses en face, fit remarquer Candiano, tu as déjà vingt-huit ans et la pratique des affaires ne s’improvise pas !

— Et Dandolo !? Il s’est bien découvert une vocation de conquérant après quatre-vingt ans, non ? Quant à moi, je n’ai nullement l’intention de récupérer Constantinople, je veux juste faire une bonne opération financière ! C’est ce que font des centaines de commerçants ici, tous les jours !

L’oncle lâcha prise et promit d’en parler à Tomasini.

Ce dernier ne rechignait pas à s’engager sur des affaires en marge de son activité d’armateur et se sentait  lui aussi un peu de la race des grands marchands. Il pouvait bien jouer trois cents ducats pour s’amuser, et il accepta de financer l’achat de la résine sous condition de prendre la moitié des gains de la revente et de contrôler qui dirigerait l’équipage du navire, quand bien même il ne s’agissait pas du sien. Des Dalmates ou des Istriens à la rigueur, mais Il ne voulait certainement pas d’un de ces patrons Vénitiens qui ne savent pas tenir un compas !

C’était à prendre ou à laisser. Les perspectives de gain se réduisaient mais y avait-t-il d’autres choix ?

Dans les mois qui suivirent, Nino engrangea assez de revenus pour accélérer les rotations avec Ceuta et son chanvre trouva bien d’autres emplois que le seul soulagement des nausées. La cité était au point culminant de sa prospérité. L’unique préoccupation des membres du conseil était de fréquenter des artistes tandis que les doges se contentaient d’assurer le service minimum pour les fêtes de la Sensa. Une partie de la jeunesse, après que la peste eut frappé, se trouvait à la tête de belles fortunes et voulait savourer la vie que les plus vieux avaient laissé échapper. Cette aristocratie de rentiers aspirait à découvrir des plaisirs nouveaux, à se détendre en somme et Nino disposait de tout ce qu’il fallait pour la satisfaire.
Il ne lui restait plus qu’à accomplir son travail d’honnête marchand.

Due anni dopo … 

— C’est un alanguissement généralisé Monseigneur ! On pourrait y deviner la main des ottomans !

Comment la physionomie de la meilleure société Vénitienne avait-t-elle pu se modifier en deux ans à peine ?
“Les plus grands désordres sont ceux qui rampent” commentaient avec lucidité quelques grandes familles de patriciens. Ces dernières pressentaient que l’âme conquérante et la soif de richesses risquaient de s’amollir et décidèrent qu’il fallait stopper le mal rapidement. La république exigeait une main ferme !

A l’issue d’une réunion secrète de commerçants, à l’heure habituelle où se déclenche un vacarme de cloches signalant la tombée de la nuit, un billet fut glissé dans la bocca du palais ducal puis dans plusieurs autres bouches de la ville. Celles-ci étaient particulièrement voraces et avalaient sans distinction les lettres de dénonciation comme les rumeurs lointaines apportées par le vent du large ou la brume des lagunes .

Fichier:(Venice) Bocca di Leone in the Doge's Palace.jpg — Wikipédia

Le lendemain tombait un jeudi. Durant le déjeuner de cette journée-là, personne ne dérangeait le procurateur pendant qu’il mangeait le Risotto al Nero que lui apportait sa mère. Pourtant, le secrétaire ouvrit la porte, les bras encombrées d’enveloppes :

— Excusez-moi Monsieur, mais là, il y en a beaucoup. On vient de les trier et …

— Rentrez Giovanni, posez-les sur la table. Aaah ! c’est mieux que la semaine dernière ! Je commençais à douter de l’esprit civique de cette ville !
Il parlait avec sa langue encore toute noire d’encre et lisait avec avidité quelques lettres d’une main.
Tomasini ?.. fichtre, c’est un gros morceau songea-t-il. On a bien besoin de ses bateaux contre les turcs et il serait malvenu de le faire tomber. Surtout ne pas s’échauffer … le Conseil des Dix tranchera lui-même !

— Commencez donc par instruire un dossier sur cet individu Nino Contari, finit par conclure le procurateur.

Tomasini, prévenu discrètement par un membre du conseil, envoya un signal à Candiano. Il fallait à tout prix que son neveu devenu encombrant soit exfiltré d’une manière ou d’une autre.

— Nino, Les choses tournent mal. Une enquête sur tes activités est en cours et tu risques de finir sur une galère identique à celle qui est venue transporter ta merde, mais avec le rôle de rameur cette fois-ci, tu comprends ? Tu dois fuir, et vite ! Il y a un convoi qui part pour Bruges. Installe-toi là-bas et fais-toi oublier. Tu y trouveras bien de quoi vivre.

— Bruges !? Ce port à demi-ensablé ? Ce n’est plus qu’une femme sèche, juste bonne à vendre ses souvenirs !

— Garde ton ironie pour des jours meilleurs. File et bonne chance !

Nino sortit de l’Arsenal et caressa une dernière fois la lionne dont le regard pétrifié, dirigé immuablement vers le sud, guettait l’arrivée d’un possible ennemi.

En s’éloignant vers la Riva degli Schiavoni, son esprit se figea soudain sur une vision de Rhodes lorsqu’il était parti là-bas réparer un bateau. L’air chaud sur sa peau, le parfum têtu du jasmin et le bleu profond du ciel, si bien collé à la mer qu’une main n’aurait pu s’y glisser. C’était là-bas qu’il fallait retourner !

Il rassembla quelques affaires, et s’engagea comme calfat dans la première nef qui partait pour Chypre. L’Orient ouvrait ses bras et Nino s’y jeta tel un amant qui s’abandonne.

Ciao Venezia ! Ciao ma belle !


(1) Le lion de Delos ne fut installé devant l’Arsenal qu’en 1716, soit bien après l’époque à laquelle se situe ma nouvelle, mais je tenais absolument à lui faire une place.

Objet

C’est l’histoire d’une application de SAV (service après vente) réalisée initialement avec Oracle APEX et complétée ultérieurement avec des module de Google Workspace. Cela a permis de constituer deux sous-systèmes étanches, l’un destiné au backoffice pour les techniciens chargés du support et l’autre destiné aux interactions avec les clients du SAV.

Oracle APEX est utilisé en version free tier (avec des limitations mais … gratuit !) dans la mesure où il y a un petit nombre d’analystes (inférieur à cinq dans le contexte de mon projet) et donc de faibles contraintes de concurrence d’accès et de charge CPU. Toutes les interactions avec les clients sont gérées avec des formulaires Google Forms, des scripts, des envois/réceptions de mail et du stockage de pièces attachées sur Google Drive.

Schéma général d’architecture:

Sous-systèmes

Sous-système de backoffice

Je suis parti d’une application standard qui était fournie avec les précédentes versions d’Oracle APEX et qui s’appelait Incident Tracking. Puisque cette application n’est plus disponible dans les version récentes d’APEX, je l’ai récupérée depuis une version 18.x. Cela m’a permis de réutiliser le modèle de données qui était de qualité et de proposer un démarrage rapide. Mais au fur et à mesure, il s’est avéré que l’écart fonctionnel était plus grand que prévu. Rétrospectivement, je serais plutôt parti d’une feuille blanche, tout en conservant le modèle de données.

Liste des tickets ouverts

L’allure générale est très proche de celle d’origine, mais il y a eu beaucoup de règles métiers spécifiques qui ont été rajoutées et qui ont fait bouger également le modèle de données. Toutes les informations du clients sont récupérés, en particulier, depuis un ERP par appel d’un service Web. Pour le reste, il s’agit d’une application classique de gestion.

Résumé d’un ticket

Les éléments de conversation entre le(s) technicien(s) et le client sont rassemblés dans un onglet Conversations et sont issus du sous-système d’interactions, tel que décrit plus loin.

L’onglet Documents contient la liste des liens vers Google Drive dans le cas où le client aurait envoyé des fichiers descriptif du problème sous forme de photos et vidéos.

J’ai profité des nouveautés des version récentes d’APEX, en particulier la recherche par facettes, pour améliorer l’ergonomie et j’ai rajouté la possibilité de se connecter avec son compte Google Workspace puisque chaque technicien en disposait.

Fonctionnement sur mobiles

Très peu de modifications ont été nécessaires pour obtenir un fonctionnement correct sur mobile. J’ai simplement utilisé des classes (hidden-xxs-down et hidden-xs-up) pour switcher sur une présentation plutôt qu’une autre au sein d’une même page. C’est typiquement le cas pour les listes de type « Interactive Grid » qui auraient imposé du scrolling sur un petit écran et auxquelles j’ai substitué des list Views.
Les autres composants Oracle APEX s’adaptent automatiquement à la géométrie du device.
J’en ai profité pour mettre un peu de PWA pour gagner un peu d’espace sur l’écran du mobile et proposer une icone plus naturelle qu’un raccourci du Navigateur.

Sous-système de FrontOffice

Ce deuxième bloc est destiné aux interactions avec les clients finaux. Il repose sur l’emploi de plusieurs modules de la suite Google Workspace. La compagnie disposait déjà d’un abonnement à ce service pour la messagerie et il n’a pas été nécessaire de souscrire à des options complémentaires.

Formulaires d’interaction

Concevoir et mettre à disposition des formulaires d’ouverture de ticket ou de demande de statut directement sous Oracle APEX aurait entrainé la communication de l’url du backoffice auprès de chaque client. Un contournement éventuel aurait consisté à utiliser le Load Balancer disponible dans le tenant en reverse Proxy et d’y placer des règle de routage en fonction de l’application à utiliser. A l’époque, je n’étais pas familier avec la technique pour le faire et, de toute manière, cela aurait posé le problème de la charge du système car si je connaissais le nombre d’utilisateurs internes (les analystes), je ne connaissais pas le nombre de clients qui se connectent à un instant donné. Un nombre élevé de connexions aurait entrainé la fermeture arbitraire de sessions déjà ouvertes, puisqu’il y a un pool limité dans le cas de Oracle Free Tiers, et cela aurait pénalisé l’accès par les technicien comme le laisse supposer l’extrait suivant des limitations:

The HTTP interface of Always Free APEX Service is rate limited to restrict the number of simultaneous service users. Approximately 3-6 simultaneous users can be supported across all of APEX, Oracle REST Data Services, and SQL Developer Web

J’ai donc fait le choix d’un sous-système complétement séparé, responsable uniquement des interactions avec les clients. Google Forms était un choix disons … spontané, puisqu’il était question de formulaires.

Tous les formulaires ont été conçus avec Google Forms. Autant le préciser de suite, ce service souffre de beaucoup de limitations car il est impossible, par exemple, de proposer une liste de valeurs dynamique. Il n’est pas possible non plus d’afficher un champs en mode read-only. Il a fallu donc s’accommoder de nombreuses limitations. C’était le prix à payer pour avoir l’esprit tranquille quant aux considérations de sécurité et de montée en charge. Il existe bien sûr des plugins spécialisés pour Google Forms mais ils sont payants ou bien apportent une publicité intempestive.

Avantages d’utiliser des formulaires externes

Le client n’ a jamais connaissance de l’existence de l’url de backoffice. Cela évite une publicité non désirée du système. Tout ce qu’il voit, c’est l’interface Google Forms qui, je le suppose, est suffisamment protégée contre des attaques de type DOS (Denial of service).

Un autre avantage indirecte est que toutes les données saisies sont enregistrées dans une base interne à Google et peuvent être également copiées automatiquement dans un tableau (sheet). Cela constitue un backup des informations entrantes dans le cas ou le SAV viendrait à être indisponible.

Design des formulaires

Comme la compagnie opère avec une marque blanche et des trois distributeurs, il fallait que le bandeau affiche une marques parmi les trois.
Dans chaque formulaire, il a fallu transporter le contexte sous la forme d’un champs de type texte.

Formulaire de création de ticket auprès du Support
Formulaires
  • Ouverture de ticket (trois variantes différentes selon le bandeau)
  • Réouverture de ticket
  • Demande de statut
  • Demande de fermeture
  • Demande de réouverture
  • Réponse à une question du SAV

Chaque formulaire est accompagné d’un script écrit en langage Google Apps Script, qui effectue la collecte des champs remplis et qui effectue un appel de service REST synchrone auprès du SAV via ORDS (cf exemple en annexe). Ce même script notifie le client par mail du résultat de l’appel.
Il y a trois versions de formulaire et le script a été mutualisé dans une librairie.

Comme il n’y a pas d’authentification, les formulaires de demande de statut, par exemple, utilisent un contexte qui doit être transporté par le formulaire. Ce contexte est une chaine de caractère qui contient le code du ticket et une valeur de contrôle.

Contexte

Il est composé du code du ticket sur cinq caractères et d’un code de contrôle (hash code à partir d’informations variées). Il fallait proposer une valeur simple à retenir pour le client afin de faciliter sa communication avec le SAV, mais aussi décourager toute tentation de tester d’autres valeurs de tickets.

Comme le champs ne peut pas être en Read Only, rien n’empêche l’utilisateur de modifier le contenu de ce champs. Le risque se limite au fait que le script qui se déclenche après la soumission du formulaire renverra un contexte que le service REST du SAV ne saura pas retrouver.

Conversations et Notifications

Les conversations et notifications s’appuient sur l’échange de mails. Il est envisageable d’avoir recours a des message SMS. J’ai testé le service OVH mais cela n’a pas été mis en production sur ce projet.

Les mails sont générés automatiquement par les scripts attachés aux formulaires Google à l’exception du cas où c’est un technicien qui décide d’envoyer une question.

L’analyste n’utilise donc que l’interface de backoffice pour effectuer son travail de tous les jours. Lorsqu’il doit demander des informations complémentaires pour instruire le traitement d’un ticket, il passe par une page standard de l’application SAV. La soumission de la page déclenche alors l’appel à un script PHP d’envoi de mail. (cf schéma d’architecture plus haut).

Lors du début du projet, la version Oracle Free Tier à la différence de sa version payante ne permettait pas l’envoi de mail , c’est à dire qu’il n’y avait pas moyen d’invoquer une passerelle SMTP. En conséquence, le contournement a consisté à se priver des fonctionnalités natives d’Oracle APEX et envoyer des mail via l’API REST de Google Gmail. Celle-ci implique une authentification avec signature des échanges mais je ne suis pas parvenu à utiliser les algorithmes de chiffrement sous PL/SQL pour déclencher OAUTH2 et il n’existe pas de package de connexion pour le PL/SQL proposé par Google. Les tentatives d’obtention d’un token valide (https://oauth2.googleapis.com/token) ont échoué avec la fonction de chiffrage dbms_crypto.SIGN_SHA256_RSA. et je me suis donc replié sur l’emploi d’une Gateway qui effectue l’authentification et l’envoi via du code PHP. Dans ce cas, Google fournit une librairie PHP prête à l’emploi, Google Client Library for PHP. J’ai utilisé une instance compute disponible dans le tenant free Tier pour héberger cette passerelle et je l’ai munie d’un certificat SSL pour permettre la communication avec l’instance APEX.

Actuellement, (décembre 2022), ces limitation ont été levées en partie puisqu’on peut désormais envoyer des mails avec certains seuils d’usage, mais qui auraient été acceptables dans le cadre du projet (cf annexes).

Types de mail
  • Confirmation d’ouverture de ticket
  • Demande de réponse à une question du SAV
  • Fourniture du statut d’un ticket

Tous les mails contiennent dans leur objet, le contexte (Code Ticket et code de contrôle) et proposent dans leurs bas de page deux boutons permettant de demander, soit le statut du ticket soit sa fermeture, soit sa réouverture.

Donc, le client final n’a jamais l’initiative d’envoyer un mail au SAV. Il peut en revanche compléter son ticket avec des photos ou vidéos, par réponse au mail initial de notification d’ouverture d’un ticket.

Mail récapitulant la création d’un ticket
Extraction des pièces attachées

Régulièrement, un script Google basé sur un timer se déclenche toutes les x minutes pour scruter les mails qui seraient munis de pièces attachées. Si c’est le cas, les fichiers sont recopiés dans un dossier de Google Drive nommé avec le nom du ticket et un appel REST informe le système qu’un pièce attachée est disponible (url de Google Drive). Ce mécanisme ne peut pas être synchrone (cette limitation est documentée par Google) et c’est la raison pour laquelle il faut effectuer du poling.

Alternative: Il aurait été possible d’utiliser un service d’intégration tel que Zapier ou Make (Integromat), mais cela aurait induit des couts d’usage.

Lorsque le technicien souhaite obtenir un complément d’information, il utilise l’application de backoffice pour rédiger la question et c’est le module de SAV qui effectue l’envoi de mail. Dans le corps du mail, il y a un texte générique et un bouton qui redirige vers le formulaire de réponse à une question du SAV.

Mail demandant des informations complémentaires

L’appui sur le bouton « Cliquer ici pour répondre » redirige vers un formulaire Google tel que celui-ci:

Formulaire pour saisir une réponse à une question du SAV

Lorsque le client veut accompagner sa demande d’ouverture de ticket par des photos ou vidéos, il doit les attacher en réponse au mail de confirmation qu’il aura reçu du SAV.

Annexes

Exemple de script accompagnant un formulaire Google

Cet extrait de script Google illustre comment sont consommées les informations issues du formulaires, juste après sa soumission.
On voit ici un appel à un service REST de création de ticket dans SAV et l’envoi d’un mail de confirmation au client dans le cas où le statut de retour est ok.

Un simple mécanisme de clef de sécurité (API_KEY) permet une reconnaissance par le service REST qui est appelé.

function createTicket(e, canal) {
// ------------------------------------------
// Creates a new ticket in SAV subsystem
// and sends a mail to the customer
// Gpmfactory 2022
// ------------------------------------------
...
var url = '<SAV REST CREATE TICKET ENDPOINT>';
var formData = {
  'EMAIL': email,
  'NAME': t_name,
  'CUSTOMER': t_customer,
  'PHONE': t_phone,
  'DESCRIPTION': t_description,
  ...
  'API_KEY': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
};

var options = {
  'method' : 'POST',
  'payload' : formData,
  'muteHttpExceptions': true
};
...
var response = UrlFetchApp.fetch(url, options);
var resp = response.getContentText();
rc = response.getResponseCode();
if (rc == "200" || rc == "200.0" ){
    MailApp.sendEmail(email, 'Ticket N° ' + resp, body, {'htmlBody' : body }); 
    Logger.log('Mail envoyé'); 
  }
...

Limites d’Oracle Email Delivery Service pour Free Tier

A la date de décembre 2022
Source

Customers that sign up for a free Oracle Cloud trial are limited to:

  • A volume of 200 emails per day where an email is defined as either a single recipient in the TO:, CC:, or BCC: fields, or a 2 MB chunk of data.Email examples:
    • A single request with 10 recipients (TO:, CC:, or BCC:) equals 10 emails.
    • A 10 MB email sent to a single recipient is equal to 10 MB divided by 2 MB per email. This equals 5 emails.
    • A single email request with a message size of 10 MB sent to 10 recipients is equal to 10 MB divided by 2 MB per email multiplied by 10 recipients. This equals 50 emails.
  • 2,000 approved senders.
  • Each user is limited to a maximum of two SMTP credentials.
  • Sending rates are limited to 10 emails per minute.
  • Inline attachments and external attachments.
  • 2 MB maximum message size including base64 encoding and headers

Last revision: 27 sept 2022

The purpose of this project is to give to business users a no-code tool for generating forms.

This project called GFM Builder is at prototype stage. It relies mainly on API wich are not documented, even if there are widely used in APEX export App files (before 22.1 version). This approach is clearly no supported, so this tool must be considered as a playground and used in a sandbox only.

The application GFM Builder is available from Github.

Target audience

GFM Builder addresses mainly companies intranet requirements but citizen portals needs as well.

A common use case is to trigger an administrative process based on informations provided by an employee or a citizen. ie: renewal contrat, ordering internal good, signaling problem in a district, etc.

This tool will leverage, in a next version, the new approval task process which can be embeded in a page.

Once these limitations given, let’s see the functionnal aspects.

High level design

Persona and Role definitions

GFM Builder is dedicated to business users.
That means that we haven’t to declare users as APEX developers. Users will be able to author and publish forms without any knowledge of Oracle APEX.

We consider the following roles:

  • Regular APEX Roles
    • APEX Workspace Administrator
    • APEX Developer
    • APEX End User
  • Application Roles brougth with the tool
    • GFM Builder Administrator
    • GFM Builder Developer
    • Final user ( means an APEX End User or an anonymous user)

The GFM Builder Role is implemented as a Contributor Role in the APEX simplifed ACL model.

The GFM Builder Administrator role is maped on Admin Role

In order to build a form, the GFM Builder Developer role must be granted to an APEX End user.

Generated Form Samples

Below are sample of forms generated with GFM Builder.

In this case, we choosed to display fields grouped by sections (GENERAL, RH, ..)

A form can embed a detail array for multivalued informations. In this case, it’s a historic of entries.

Using a detail array inside a form

Apps Store

A sample catalog application (GFM Store) is provided as an exemple. Each time a form is tagged as Published, it will be displayed in this catalog.

Data collecting

All data are stored in a unique table GFM_DATA.

The data can be displayed by GFM Builder Admin and Forms owners.

Final users will not be able to display data. We suppose that a process will send data to the user, by any feedback way, email for instance. It’s not in the scope of the project.

Simplified report displaying data collected through a form

Form design

The design process is mainly declarative. It’s a matter of designing the items which constiture the form.

Instead of creating a data set then building the form (in in standard way), we create there both the data set and the form in the same user interface.

A form is comprised of items. An items is either a simple attribute (like text Field, date) or a multivalued attribute. This second kind of items is implemented as an Interactive Grid. This technical choice provides more flexibility during fields filling.

Items must be pick up from a predifined collection of components which are:

  • Basic items
    Text,Number, Date, email, Inline List (List of values declared inline)
  • Predifined List of values
    These lists are created in a dedicated page called ‘Components’, either by an administrator or the user himself.
  • Grids
    It’a an existing form wich is declared as been usable as a grid inside an other form. ie: Historic of positions, list of contracts, etc.

Reusing existing form definitions

It’s possible to copy all items of an existing form into an other form.

  • Inline declaration
    value and label are given the same value.
  • Specification declaration in component module
  • External declaration
    per declaring the name of the external table or view.

Sections

A section is a logical group of items which will be rendered as either a tab or a paragraph, depending a preference.

Fields are spreaded in sub-regions if there are two sections at least referenced by items. Navigation is provided by tabs, either with regular tabs or tabs and a first overall tab.

Example of navigation with preference $REGION_SECTION =DISPLAY_SELECTOR

User Interface

The user interface is very classic with very minimum usage of Drag n Drop. This is a weakness if we compare with other no-code tools on the market. But on an other hand, the tool priviliges flexibility in rendering, that is not the case with « Google Forms », for instance.
At the minimum, it’s possible by Drag’n Drop to re-order displayed items.

Rendering

Very Minimal Decoration

This image has an empty alt attribute; its file name is image-8-1024x547.png
Minimal decoration

Generation options

a set of preferences drives the generation process. We have reused APEX terminology in order to be in line with the regular Developer Interface.

For the moment, there are a few preferences:

  • $ITEM_TEMPLATE (FLOATING, ABOVE, BESIDE)
  • $ITEM_HELP (Y,N)
  • $PAGE_TEMPLATE (MINIMAL_NO_NAVIGATION)
  • $REGION_SECTION (STANDARD, TABS_CONTAINER,DISPLAY_SELECTOR

Below are some variants depending on « Navigation between sections » preference

Section in a standard rendering

Sections rendered with Tabs

List of Values

The creation of list of values is done by creating a new component with a LIST type. Then, a second page is used for entering the values.

List of components (The predefined lists are tagged List)
Entering the values for a List of Values component.

Conditionnal displaying

An item can be displayed or not depending the value of an other item. If the driving item is based upon a List, the list of values will be proposed for choosing a specific value which will trigger displaying of slave item.

Technical design

Data Model

Implementation

  • A data model for the form/Sections/Items, preferences and components
  • A main data store (GFM_DATA), designed in the same way that the apex_collection table. There are 50 items at maximum.
  • As many views there are forms. Each view is given an instead trigger.
  • For each form, a specific view maps columns on given columns of the gfm_data table.

Generating process

  • Re-generate specific view
  • Re-generate specific Trigger
  • Generate the corresponding Page in a target Application.

The target application must belong to an arbitrary Application Group called ‘GFM Apps’ and must be choosed in the setup page. The ‘GFM Apps’ is automatically created during installation of GFM Store application.

List of Values

All the data are stored in a dedicated table: GFM_LOV.

Objects are named in Oracle database with GFM_ prefix

Lifecycle

If an item is renamed, there are no impact in the mapping

If an item is dropped, the mapped column in the GFM_DATA table is preserved, and no other item won’t be mapped on it. The reason it that we want avoid to display old data with the new item.
An option menu allow to reset data and column mapping.

Security

Before form are deployed, they must by set « Published » state.

A form can be accessible either in a anonymous mode (no need to be authenticated) or in an authenticated mode.

because GFM Builder relies intensively on internal APEX API, some important flags have been set:

A compagnon Application called ‘GFM Store’ is provided as an example of an Apps store.

Appendices

Non documented APIs used in this project

  • wwv_flow_api.create_page
  • wwv_flow_api.create_page_plug
  • wwv_flow_api.create_page_button
  • wwv_flow_api.create_page_branch
  • wwv_flow_api.create_page_item
  • wwv_flow_api.create_page_process
  • wwv_flow_api.create_page_da_event
  • wwv_flow_api.create_page_da_action
  • wwv_flow_api.create_region_column
  • wwv_flow_api.create_interactive_grid
  • wwv_flow_api.create_ig_report_column

APEX export API have been rewritten in 22.1 (wwv_flow_imp). That means that the previous API are not garanteed to be working in the future, and therefore neither is this tool GFM Builder.

Plugins used in the project

This material should be implemented as a region Plugin but not enough time and overall, it’s quite easy to adapt it manually.
Approach used is to generate button from a pl/sql code and implement the needed JS code.

Another approach using regular APEX buttons with Dynamic Actions did’nt give acceptable result.
Color scheme is picked from Redwood Light look and feel.

Code

Create a region PL/SQL dynamic content

Add a page Item Pnn_RATE

Add the Following code intro the PL/SQL code property and change :P6_RATE by your item page name and choose between three or five emoticons (l_nb number :=5)

DECLARE
    l_css apex_t_varchar2;
    tout varchar2(32000) := '';
    l_emoji apex_t_varchar2;
    l_size varchar2(100) := ' fa-2x fa-lg ';
    l_gap varchar2(100) := '' ;     -- gap between two icons
    l_js varchar2(32000);
    l_rate varchar2(2);     -- rate value
    l_nb    number :=5;     -- number of emoji (3 or 5)


BEGIN
    l_rate := :P6_RATE;
    htp.p('
        <STYLE>
            .btn {
                padding: 0;
                border: none;
                background: none;
                font-size: 10px;
                cursor: pointer;
            }
            .btn1 {
                background: radial-gradient(#d63b25 50%, transparent 50%);
            }
            .btn2 {
                background: radial-gradient(#ac630c 50%, transparent 50%);
            }
            .btn3{
                background: radial-gradient(#d8d0b5 50%, transparent 50%);
            }
            .btn4{
                background: radial-gradient(#c2d4d4 50%, transparent 50%);
            }
            .btn5{
                background: radial-gradient(#bdd9ae 50%, transparent 50%);
            }
            
            .btnsel {
                padding: 0;
                border: none;
                color: white;
                //background: none;
                background-color: silver; //#bac9ba;
                font-size: 10px;
                cursor: pointer;
            }

        /* Darker background on mouse-over */
        
        .btn:hover {
            background-color: silver;
        }
    </STYLE>  
    ');
    if l_nb = 5 then
        l_emoji := apex_t_varchar2('fa-emoji-frown','fa-emoji-slight-frown','fa-emoji-neutral','fa-emoji-slight-smile','fa-emoji-sweet-smile');
        l_css := apex_t_varchar2('btn btn1','btn btn2','btn btn3', 'btn btn4', 'btn btn5');
    elsif l_nb = 3 then
        l_emoji := apex_t_varchar2('fa-emoji-slight-frown','fa-emoji-neutral','fa-emoji-slight-smile');
        l_css := apex_t_varchar2('btn btn1','btn btn3','btn btn5');
    end if;
    
    -- Displaying the icons
    
    tout := '<div>';
    if l_rate in (1,2,3,4,5) then
        l_css(l_rate) := l_css(l_rate)|| ' btnsel';
    end if;
    for i in 1..l_nb loop
        
        tout := tout ||
        '<button type="button" id = "pb' ||i || '" class="'||l_css(i) ||'" ><i class="fa '||l_emoji(i) || l_size ||'" ></i></button>'|| l_gap
        ;
    end loop;
    tout := tout || '</div>';
    htp.p(tout);        -- renders html markup

    -- Builds JS code

    l_js := '
    <script type="text/javascript">
        function clearcss () {
            for (i=1;i<=5;i++) {
            document.getElementById("pb" + i).classList.remove("btnsel");
            }
        }
        function setRate(pnro) {
            apex.item( "P6_RATE" ).setValue( pnro, null, true );
            clearcss();
            document.getElementById("pb" + pnro).classList.add("btnsel");
        }';
    -- adding Listeners
    for i in 1..l_nb loop
        l_js := l_js || '
        pb'||i||'.onclick = function() {
            setRate("'||i||'"); 
        }            
        ';
    end loop;
    l_js := l_js || '</script>';
    htp.p(l_js);

END;

Below, a color palette get using block css (u-color-nn) in a card template. Goal was to display scorecards with differents look and feels. CSS class has been set at the Card CSS classes level in the Attributes tab

For comparaison, the same palette with VITA Slate theme style. (Other VITA styles render colors identically)

J’ai eu l’occasion de faire une présentation sur le Low-Code et j’en ai profité pour faire auparavant un tour de l’offre des produits.

J’avais deux cas d’usage :

  • Publication d’un catalogue d’œuvres d’arts avec recherche et modification (CRUD classique)
  • Mini-Service après-ventes avec saisie des incidents via un formulaire et suivi des tickets sur une interface adaptée à un mobile

Dans la catégorie « Low-Code » Je connaissais essentiellement Oracle APEX

Je me suis plongé dans les autres produits suivants :

  • Microsoft Power Apps
  • Google Appsheet
  • OutSystems
  • Salesforce Lightning
  • Zoho Creator
  • Bubble
  • Glide
  • Zapier
  • Integromat
  • Airtable

Ce qui suit traduit mon ressenti. Ce n’est absolument pas un jugement sur les produits car ce ne serait pas leur rendre justice sans avoir été formé correctement au préalable.

Pour chacun, j’ai ouvert un compte trial, et cela n’a présenté aucune difficulté. A aucun moment je n’ai été obligé de présenter ma carte de crédit.

Microsoft PowerApps

PowerApps est parfaitement intégré dans l’écosystème Microsoft, évidemment.

Hyper complet et précis. Du bon ouvrage comme Microsoft sait faire. Des connecteurs à gogo Pas très intuitif, cependant.

Google Appsheet

Nouvelle mouture de leur outil de dev. Très facile d’emploi. On obtient un résultat de très bonne qualité sur mobile avec une intégration bluffante avec Maps ou une lecture intelligent de badge. Très bonne illustration du concept de no-code. J’apprécie énormément ce produit.

OutSystems

Pure player du low-code. Solide. Approche avec un IDE en local+ déploiement. Conçu à l’origine à Lisbonne. Du travail solide de « maçon ».

Salesforce Lightening

On rentre dans un monde préconstruit avec des objets métier prêts à l’emploi. Services de type CRM, vente, Field Service etc. et possibilité de créer ses propres applis en utilisant le même framework. C’est très impressionnant. Grand Clickodrome ! Modèle d’habilitation remarquable. L’écosystème est très stimulant car il est fait de telle manière qu’on peut gagner des points en augmentant son apprentissage.

Zoho Creator

Pas mal. J’ai survolé le produit.

Bubble

Typique du low code. Je n’ai fait que survoler le produit

Glide

Petit coup de cœur par rapport à la simplicité d’emploi et au résultat obtenu.

C’est du no-code pur. C’est le produit qui m’a fourni un catalogue d’œuvres d’art prêt à l’emploi sans que je n’ai à rien ajouter de plus. Glide a compris que certaines url contenaient des liens vers des images et il les a utilisées ainsi

Zapier

Service d’intégration. Le leader du domaine. Hyper simple à utiliser

Integromat

Service d’intégration. Sur le même domaine que Zapier. Très simple d’utilisation.

Airtable

Inclassable car il s’agit d’une sorte de mid-end qui ne s’occupe que de gérer les données dans des tableaux analogue à des sheets. Possibilité d’établir des associations entre les tableaux. On peut y ajouter des triggers qui déclencheront des actions. Google prépare un produit similaire qui s’appellera Google Table.

Le couple Airtable/Zapier est, par exemple, un bon moyen pour bâtir un backend gérant les données et les processus.

Oracle APEX

Je termine par celui que je connais le mieux. Je suis toujours épaté par le bon compromis proposé par ce produit. Le curseur est au bon endroit : Du no-code pour ceux qui veulent générer une application à partir d’un tableau Excel, par exemple, mais aussi la possibilité de construire une application plus sophistiquée, sans aucune limite.