Ci-dessous des questions/Réponses faisant suite au web Séminaire du mardi 30 juin 2020 organisé par Oracle.


Bonjour, Je suis intéressé par la précision que Patrick voulais faire sur l’affichage des liste sur mobile.
Le composant « Interactive Report » est très riche et il est bien adapté pour un écran de type Desktop. Cependant il est plus commun de proposer, sur un mobile, une liste simplifiée évitant le recours à du scrolling latéral. Il vaut ainsi mieux utiliser un autre type de liste qui s’appelle « List view » qui n’affiche la valeur que de quelques colonnes.

Au niveau implémentation, on peut :

  1. Soit installer les deux composants sur la même page et le rendu de l’un ou de l’autre sera sous le contrôle d’une simple classe de style.
    cf https://apex.oracle.com/pls/apex/apex_pm/r/ut/responsive-utilities
    classes : hidden-xxs-down et hidden-xs-up.
    C’est la « magie » du framework Bootstrap qui permet cet affichage conditionnel.
  1. Soit créer deux pages différentes avec dans chacune d’elles le composant adapté, installer un code javascript de détection de type de périphérique et router la demande de liste vers l’une ou l’autre des deux pages.

De façon plus générale, voici quelques liens utiles pour le développement destiné à des mobiles:
cf les patterns APEX pour les mobiles
Cf deux demos de Shakeeb Rahman, Product Manager APEX, autour d’APEX et des mobiles

Bonjour, quel fonctionnement en mode déconnecté ?
Ce mode n’est pas prévu en standard dans le produit APEX, mais cela est possible.
Il faut avoir recours à l’emploi du framework PWA qui permet de mettre en place un fonctionnement en mode déconnecté … au prix, il est vrai d’un peu (!) de programmation en Javascript.

Cf le très bon post de Vincent Morneau (Insum) sur le sujet: http://vmorneau.me/apex-pwa/

Comment dois-je faire pour développer des applications présentes dans une base de données autres que Oracle?
La société www.troiso.fr a évalué et documenté l’emploi d’un passerelle ODBC (Heterogeneous Services inclus dans le SGBD Oracle) pour se connecter à des données présentes dans une base PostgreSQL, ainsi que d’autres.
Sinon, il faut songer à exposer les données via des Web Services (REST ou SOAP) et les consommer dans APEX.

Est il possible de travailler avec des workflows d’approbation (du style BPM) ?
Oui, on peut déclencher des Workflows en effectuant un appel via un WebService (REST ou SOAP).
Si c’est Oracle

On peut également, depuis un Workflow externe, rentrer dans une application APEX dans une page spécifique avec le pre-positionnement d’un contexte. (deep linking).

Et il est bien sur possible d’appeler un service REST manipulant des données gérées avec une application APEX depuis un outil BPMN. Voir cet exemple avec Oracle PCS. Dans ce cas, il faudra developper un module REST soit dans l’interface d’Oracle APEX, soit via Sql Developer.

Par ailleurs, même s’il n’existe pas de capacité de workflow dans APEX, il existe des expérimentations intéressantes de couplage avec un framework open source de type BPMN. cf https://apex.mt-ag.com/apex/f?p=SHOWCASE:11:0&p_lang=en (basé sur bpmn-js qui est un sous-produit open source de Camunda)

Est-ce qu’on peut créer une application qui utiliseraient des données de différentes bases de données (Plusieurs moteur, un moteur oracle, aller chercher des données dans une base SQL Serveur, d’autres dans une base My SQL) ?
oui.

La société www.troiso.fr a évalué l’emploi d’un passerelle ODBC (Heterogeneous Services inclus dans le SGBD Oracle) pour se connecter à des données présentes dans une base PostgreSQL.
Sinon, il faut songer à exposer les données via des Web Services (REST ou SOAP) et les consommer dans APEX.
Tant qu’on est en lecture, tout va bien. Si on doit coordonner une transaction sur plusieurs systèmes, le problème se corse car il faut que les systèmes candidats supportent le « two-phase commit. »

Est-ce qu’on remplacer l’url par notre nom de domaine ?
Oui. S’il s’agit d’une instance Autonomous Database (appelée aussi ATP), il faut installer un custom ORDS (uniquement à partir de la version 19.4) sur une VM de type compute afin de se substituer au module ORDS utilisé par défaut. (ce dernier n’est absolument pas modifiable. C’est une sorte de boite noire)
En général, on installe un simple tomcat avec un Apache ou un Nginx en Reverse proxy, puis on installe un certificat afin de travailler en SSL. Un Load Balancer est également disponible sur la plateforme Free Tier.
Enfin, on redirige son nom de domaine (Record de type A) vers la VM.
https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/19.4/aelig/installing-and-configuring-customer-managed-ords-autonomous-database.html#GUID-24F4D51F-0BAF-4D3B-A714-492ED02D1212

On dispose de deux VM de type Compute (micro-shape) dans l’environnement Free Tier. Donc tout va bien pour tester le concept !.

cf l’excellent blog de Dimitri Giellis

Même une base My SQL ou SQL Server, en gros autre que Oracle ?
oui pour la consommation des données.
Rappel: Oracle APEX DOIT être installé dans une base Oracle !

La société www.troiso.fr a évalué l’emploi d’un passerelle ODBC (Heterogeneous Services inclus dans le SGBD Oracle) pour se connecter à des données présentes dans une base PostgreSQL.
Sinon, il faut songer à exposer les données via des Web Services (REST ou SOAP) et les consommer dans APEX.
Tant qu’on est en lecture, tout va bien. Si on doit coordonner une transaction sur plusieurs systèmes, le problème se corse car il faut que les systèmes participants supportent le « two-phase commit. »

Patrick, c’est quoi le nom du plugin ? PWA, et le plugin carte ?
Le nom du plugin est: JK64 Report Map

Il est disponible depuis le site apex.world:

https://apex.world/ords/f?p=100:710:8140164595770::::P710_PLG_ID:COM.JK64.REPORT_GOOGLE_MAP_R1

Cf le très bon post de Vincent Morneau (Insum) sur le sujet: http://vmorneau.me/apex-pwa/

Peut on partir d’une modélisation pour faire notre appli ? Ou modéliser dans APEX ?
Dans APEX, on travaille au niveau du modèle relationnel. Pour modéliser à un niveau conceptuel, il faut utiliser des outils tiers. On pense, par exemple, à l’outil Oracle Data Modeler qui est basé sur une approche Entité/Association avec un formalisme de type Barker. (https://en.wikipedia.org/wiki/Barker%27s_notation)
Sinon, ou peut utiliser tout autre type de modélisation (objet par exemple) sachant qu’au final, il faut que cela se concrétise par un modèle relationnel sous forme d’un script SQL/DDL.

Peut-on intégrer de l’APEX dans du code PHP ou dans un CMS (ex: WordPress ou Drupal) ?
Oui en utilisant les deux approches suivantes:

Soit on consomme (depuis WordPress, Drupal …) des données gérées par une application APEX via des appels de services REST

Soit on embarque une page complète APEX (avec sa UI) dans le CMS en utilisant une iframe. Dans ce cas, il ne faut pas oublier d’autoriser le « deep linking » dans les propriétés de l’application Apex.

Possibilité de générer une application Native iOS ou Android ?
Non. Oracle APEX ne génère que des application en mode connecté ou semi-connecté (cf approche PWA), mais pas d’applications mobiles natives.

pour la création de compte free on ne trouve pas la region pour la France, il propose suisse, germany, uk ? quel region utiliser ?
Il faut utiliser la région de Francfort.

support apex free sur www.troiso.fr
Info donnée par Fred Brunet

Utilisable avec une base Mysql ?
Oracle APEX DOIT être installé dans une base Oracle, mais peut consommer des données depuis d’autres systèmes de base de données.

Voici un catalogue des produits developpés avec APEX https://www.builtwithapex.com/
Info donnée par Souleman Dembele

Y a t-il aussi un gestionnaire de code source pour intégré ainsi qu’un outil de build ?
Non. Il s’agit des deux points importants sur lesquels travaillent les équipes de développement d’Oracle APEX.

Pour le moment, il faut exporter régulièrement son application et l’enregistrer dans un CVS (Subversion,, Github…) Le format d’export est granulaire jusqu’au niveau Page.
La société Insum propose un outil qui va plus loin pour la granularité des objets: APEXCl
https://apexcl.dev/
Pour le build, il existe des guides de bonnes pratiques et aussi un plugin pour Maven, par exemple.(réalisé en 2014)

Y’a t-il une gestion des profiles d’utilisateurs sous APEX ?
Oui. On peut gérer des profils d’utilisateurs avec des préférences et chaque utilisateur a la possibilité de modifier celles qu’on aura exposées lors de la conception.
Les règles de complexité des mots de passe sont paramétrable.

En parallèle, il est possible aussi de créer des groupes d’utilisateur et mettre en place une gestion d’ACLs.
La méthode la plus sophistiquée pour élaborer une gestion de rôles complète consiste à utiliser l’option Oracle RAS (Real Application Security). Oracle APEX est nativement intégrée avec Oracle RAS.

cf http://gpmfactory.com/index.php/2019/12/14/oracle-apex-et-ras/

J’ai fait un rapide test sur apex.oracle.com pour vérifier la nouvelle fonctionnalité de production de rapport PDF (Report Printing) à partir d’un Interactive Grid.

Voici deux échantillons de rapport à partir d’un Interactive Grid. L’un est en orientation paysage, l’autre en portrait.

On remarque que les règles d’affichage ainsi que les ruptures sur valeur de colonne sont respectées. Il n’existe pas de possibilité d’influencer le modèle PDF. Il s’agit donc d’une fonctionnalité pratique mais qui ne peut pas se substituer à un outil d’édition sophistiqué comme BI Publisher, AOP ou FOP.

Il faut constater qu’un Interactive Report ne bénéficie pas (ou pas encore ?) de cette fonctionnalité

Objet

Tout est prévu dans Oracle APEX pour produire une application multilingues. Dans cet article, on verra comment utiliser un service en ligne de traduction pour accélérer le processus. Nous avons testé le service crowdin. Il s’agit d’un service en ligne payant permettant de traduire des entités textuelles de façon coopérative en permettant d’organiser une équipe virtuelle de traducteurs.

Principes de traduction avec Oracle APEX

Il existe un live, Expert Oracle Application Express dans lequel Francis Mignault décrit, dans un chapitre consacrée à la globalisation avec APEX, les étapes pour traduire une application en plusieurs langues.

Le principe général est :

  • Choisir une application dans sa langue de base
  • Extraire les chaines de caractères
  • Les enregistrer dans un référentiel
  • Les exporter au format XLIFF
  • Les traduire (à l’aide de crowdin dans notre cas)
  • Les importer dans le référentiel
  • Produire, sous un autre ID, une variante de l’application originelle dans la langue cible

Au final, on obtient autant d’applications que de langue cibles mais les taches de développement continuent de n’avoir lieu que sur la version principale. Les applications en langues supplémentaires existent dans le référentiel APEX avec un numéro spécifique mais ne sont pas visibles depuis la console de développement.
Remarque importante: Apres chaque modification de l’application, il faut re-publier toutes les variantes cibles.

Processus de traduction

Ci-dessous sont rassemblées des captures d’écran qui illustrent les différentes étapes. Pour des raisons de simplifications, tous les détails n’ont pas été mentionnés.
Pour mémoire, il faut garder à l’esprit qu’un travail de traduction des donnée dans la database reste à faire (listes de valeurs etc.).

Nous avons utilisé le service Crowdin pour charger et traduire automatiquement les chaines textuelles.

Ces chaines sont obtenues au format XLIFF, qui est un format standard pour le partage de données avec des outils de traduction.

Echantillons de l’application traduite

Ci-dessous quelques échantillons de l’application Customer Tracker traduite en Français, Allemand et Arabe

Conclusion

On peut très rapidement obtenir un prototype d’application APEX traduite dans une langue quelconque avec le support d’un service de traduction automatisée comme celui de Crowdin. Il existe, bien sur, d’autres services du même genre et on peut également envisager l’emploi des APIs de Google Translation pour réaliser un service « Maison » de localisation.

Oracle APEX is a very good starting point for evaluating Oracle Forms migration workload.
From the Builder home page, a very discreet menu option leads to the following page:

From there, it’s possible to create a Forms migration projet then upload successively the different FMX files. The drawback is that it can be a cumbersome task, especially when thare are hundreds of forms.

For this reason, I inspected the code in the internal flow 4400 then put all the pieces of code together and add a loop.
The FMX files will be loaded in one shot as a zip files in the static files Section of Shared Components.
In this version of tool, only FMX are taken into account.

Download form Github.

IMPORTANT: You must have SYS access privilege in order to install the package !! (so, forget it if you’re using ATP)

Once the FMX files are loaded into the APEX repository, its easy to spread the analysis tasks amoung multiple developers as we discuss in an another post.

Objet

Ce post décrit un prototype de ChatBot builder qui a été réalisé avec Oracle Application Express (Apex) et qui est destiné à Facebook Messenger. Pour illustration, j’ai créé un bot spécialisé pour un syndic immobilier qui illustre la plupart des possibilités que peut offrir un agent conversationnel de type Facebook Messenger.
On peut également imaginer des bots simplifiés pour des collectivités locales répondant à des question usuelles relatives à la vie locale, ou bien des bots spécialisés pour naviguer dans un catalogue d’objets.

La solution retenue repose sur un modèle de données qui contient les différentes « routes » de réponse ainsi que les modèles de message. Ce genre de ChatBot est à privilégier pour un ensemble fini de réponse. Il n’y a aucune intelligence ni moteur NLP. En revanche, aucune programmation n’est nécessaire: On se contente de remplir les différentes réponses en réaction à des messages.
Ce chatbot tire également parti du moteur de recherche textuel fourni par la Database Oracle et cela offre à l’utilisateur beaucoup de flexibilité dans la formulation des questions (insensibilité aux voyelles accentuées, par exemple, et stemming sur les formes conjuguées).  Il est possible de rajouter un thésaurus avec des synonymes, ou bien une nomenclature.

Lire la suite de

Objectif

L’objectif est de produire, à partir des articles d’un groupe Facebook, un magazine PDF destiné à être imprimé .

Un cas d’usage est celui d’une famille qui souhaite éditer un petit journal destiné à des personnes peu connectées sur internet et pour qui le support papier reste un média préférable. Il peut s’agir aussi d’un document rassemblant des reportages sportifs réalisés par les membres d’une association. Par extension, les principes exposés peuvent être utilisés par tout utilisateur Facebook pour la constitution d’un magazine imprimé à partir de ses publications, ou tout simplement pour exporter son contenu vers un système externe.

Approche

Le procédé qui a été choisi est le suivant:

Extraire les publications d’un groupe de façon programmatique en s’appuyant sur l’API Graph de Facebook afin de récupérer le texte, les images associées ainsi que l’auteur et la date.

Enregistrer les informations dans une database de façon à pouvoir effectuer un post-traitement (filtrage, corrections orthographiques, mise à l’écart de certaines publications selon des critères)

Retraiter éventuellement les images de type paysage afin d’obtenir un ratio différent de celui utilisé par Facebook qui est de 1,3 environ.

Produire un document au format XML ou JSON et leur appliquer un gabarit de mise en page à l’aide d’un moteur d’édition.

Principe et Architecture

  • Utilisation de l’API Graph de Facebook (REST api) pour extraire en format JSON les éléments à imprimer (photo + texte)
  • Réalisation d’une interface utilisateur avec Oracle APEX pour extraire les articles via l’API Graph, indiquer la période à sélectionner dans le groupe ou la page et produire le fichier XML intermédiaire.
  • Traitement spécifique (cropping) des images avec ImageMagik afin d’obtenir un ratio largeur/hauteur adapté pour l’impression en A4
  • Production du document PDF avec Apache FOP
  • Impression et façonnage du magazine

Les appels REST vers l’api Graph sont lancées depuis du code PL/SQL. C’est donc une procédure stockée dans la database qui coordonne les appels nécessaires pour rassembler les articles contenant la description ainsi que le lien vers l’image associée.

Architecture simplifiée

Dans le schéma d’architecture ci-dessus, les services sont identifiés par la couleur bleue. On constate qu’il y en a trois. Dans mon cas, j’ai groupé les services de traitement d’image et Apache FOP sur une même instance cloud de type compute tandis que l’interface de pilotage (Application Oracle APEX) fonctionne sur une autre instance cloud. Il est bien sur possible de tout faire fonctionner sur un simple PC en local.

Réalisation

Créer un groupe Facebook

Pour mémoire: Créer un groupe Facebook ou en utiliser un déjà existant, indiquer une visibilité de type « privé » et enrôler les membres. Seul un administrateur de groupe peut inviter d’autres membres, sous réserve qu’il ait déjà une connexion individuelle avec ceux-ci. Il est cependant possible de constituer un groupe avec des membres qui ne possèdent pas de connexion entre eux.

Créer une application Facebook

Créer une nouvelle application Facebook à partir de la console de développement. Dans le cadre du prototype, l’application a été gardée dans un mode « en cours de développement » et n’a pas été publiée.

Tester le bon fonctionnement des APIs en se connectant à la console de développement API Explorer (API Graph). Tout appel doit être accompagné d’un jeton d’accès (Access Token) obtenu après identification puis consentement quant aux privilèges accordés à l’application. cf annexes

Tant que l’application n’a pas fait l’objet d’une demande de certification auprès de Facebook, l’appel à certaines méthodes n’est pas possible. C’est le cas en particulier de la récupération du nom de l’auteur d’un article. Cela est évidemment très gênant pour la fabrication de notre magazine, puisque il s’agit d’une information éditoriale essentielle. Pour contourner cette limitation et puisque il n’était pas dans mon intention d’aller jusqu’au bout du processus de déclaration de l’application, j’ai demandé aux contributeurs du groupe d’ajouter systématiquement leur prénom sur la dernière ligne de l’article. Les prénoms et les photos de profil sont gérés manuellement dans une table.

Créer une application de pilotage

Créer une application Oracle APEX à partir d’une instance APEX existante (depuis une instance Oracle Free Tiers par exemple, qui est gratuite). L’instance https://apex.oracle.com risque d’être inadaptée car il y a un quota d’appels de services web qui est assez bas. Chaque extraction génère un nombre d’appels représentant le double environ du nombre d’articles

Appliquer un traitement d’image

Cette contrainte est liée au choix de pouvoir disposer deux articles sur une page A4. Il s’agit d’un choix de mise en page purement arbitraire. (cf plus loin sur la partie Design et mise en page)
Les images de type paysage doivent être retraitées afin d’obtenir un ratio 1,7 au lieu de 1,3. Pour cela on a recourt à un traitement de recadrage pour aplatir l’image tout en gardant les proportions d’origine. Le choix technique est d’installer un service écrit en php avec le module php_imagemagick.
Il aurait été possible d’utiliser les packages Oracle ORD_IMAGE, mais ceux-ci n’étaient pas disponibles dans l’instance Oracle Free Tiers que j’ai utilisée.

Fabriquer le document PDF

On utilise Apache FOP. Il faut télécharger le logiciel sous Windows ou Linux. Il est possible de le faire fonctionner en mode commande, standalone.

Il est important de bien paramétrer FOP afin de disposer des polices de caractères souhaitées. Pour cela, on paramètre le fichier de configuration avec la directive auto-detect (fichier de configuration fop.xconf) et on copie les polices de caractères sur le serveur Linux, si nécessaire.

<fonts>
...   
 <directory>C:\Windows\Fonts</directory>

    <!-- auto-detect fonts -->
    <auto-detect/>
</fonts>

Une tache importante de réalisation a consisté à mettre au point le gabarit de mise en page automatique. Ce template se présente sous la forme d’un fichier contenant des déclarations XSLT de formatage. Les données extraites depuis Facebook sont présentées en format XML à Apache FOP qui les fusionne avec le template XSL.

Design et mise en page

Je me suis largement inspiré du design utilisé par le service payant Famileo et j’ai utilisé les mêmes principes de formatage.
Leur choix de mise en page consiste à assembler deux articles sur une même page A4. Si l’image est verticale, le texte est placé à droite, sinon au-dessous dans le cas d’une image horizontale. La quantité de texte est, d’après leur FAQ, arbitrairement limitée à 300 caractères.

Une des conséquences est que le rapport de forme (Largeur divisée par hauteur) pour les images horizontales est de l’ordre de 1,7 au minimum alors qu’il est de 1,3, voire moins pour les images enregistrées dans Facebook. Cela explique que dans ce cas, il faut rogner les images en hauteur (cela fait l’objet du traitement de recadrage qui est effectué à la volée par le module PHP).
Pour le texte, j’ai fixé une limite plus haute que 300, quitte à réduire dans une certaine limite la taille des images selon l’article.

Echantillon de page avec deux articles

Façonnage du magazine imprimé

Il faut faire un choix sur le format de papier et cette décision entraîne un type de reliure correspondant. Le plus simple est de partir sur du format A4 « portrait » et d’imprimer en recto-verso. Les feuilles sont ensuite agrafées en cinq points sur le bord gauche et un ruban adhésif opaque (blanc ou fantaisie) et placé par dessus les agrafes.

L’autre solution consiste à partir sur du format A3 « Paysage » en mode recto/verso et d’imprimer sur deux colonnes. La reliure sera effectuée par deux points de couture au milieu des deux colonnes. L’avantage est de procurer de meilleures sensation à la manipulation puisque les pages A3 seront pliées et le magazine pourra se maintenir en position ouverte sur une table. Le problème à résoudre concerne le « chemin de fer » car les pages devront être assemblées dans un ordre qui n’est pas séquentiel:

Par exemple la première feuille physique comprend 4 pages:

  • 2
  • n-1
  • n
  • 1 (couverture)

Je n’ai pas encore étudié cette option A3.

Conclusions et enseignements

La réalisation automatisée d’un journal aboutit à un résultat qui peut être considéré comme correct mais qui n’est peut-être pas aussi attractif que lorsque la composition est réalisée manuellement.

L’avantage d’utiliser Facebook est de pouvoir constituer aisément un pool de participants, indépendamment de la tranche d’age car FB est très populaire à la fois chez les jeunes et moins jeunes. Il n’est donc pas nécessaire de réaliser un réseau social privé avec les inconvénients de changement d’habitude, confidentialité, perte de mot de passe que cela aurait pu entraîner.

La destination des publications Facebook sur un support imprimé impose une discipline dans la rédaction des articles et notamment une exigence quant à la quantité minimum du texte associé à l’image. Or, sur FB, les usages donnent une plus grande importance à l’image qu’au texte. Cela implique donc une coordination éditoriale pour inciter les participants à contribuer différemment de leurs habitudes.

D’un point de vue budget, il faut :

  • disposer d’un ordinateur sur lequel seront installés tous les softs répertoriés plus haut ou bien avoir une instance en cloud
  • Coût d’impression des pages A4 en couleur recto/verso :
    environ 50 c /feuille, soit 4 € pour un magazine de 8 pages
  • Coût d’envoi au destinataire: environ 2 €

Par comparaison, l’abonnement mensuel à Famileo et de 5,90€/mois par destinataire, ce qui en faite une solution compétitive s’il n’y a qu’un seul destinataire.

Annexes

Matériel, Code, templates

Sera mis à disposition plus tard sur Github.

Manipulations simples sur l’API Graph

  • Pour obtenir la liste des groupes:
    /me/groups
  • Liste des posts
    /<group_id>/feed?since=<date_depart au format YYYY-MM-DD>
  • Récupérer les images
    /<feed_id/attachments

Remarque: si la publication Facebook est issue d’un partage depuis un espace personnel ou bien une page ou un autre groupe, il faut effectuer un appel supplémentaire car l’élément est considéré comme une story. cf subattachments

Objectif

L’objectif est de disposer d’une liste de valeurs des codes postaux/Communes dans un formulaire afin de contrôler la saisie du nom de la commune et vérifier la cohérence avec le code postal. On profite des améliorations importantes qui ont été apportées sur les listes de valeurs dans la version 19.1 et 19.2 d’Oracle APEX, avec notamment le support de plusieurs colonnes en réception.

Approches

La première approche triviale consiste à importer la liste des communes depuis un site officiel puis à la charger dans une table.

La deuxième approche a recours à un web service REST.
L’intérêt est d’éviter la maintenance supplémentaire d’un objet dans la database. Nous allons utiliser cette méthode.

Le site https://geo.api.gouv.fr/ fournit une API en open data qui répond au besoin. Le problème est que la liste des codes postaux pour une commune est fournie sous la forme d’un tableau (nested array). Or dans la version 19.2, un web module ne prend pas en compte les nested array. Il faudrait par exemple un proxy pour aplatir la structure json.

La poste, à travers son portail datanova, fournit également une API en open data. le résultat obtenu est exploitable avec APEX et c’est ce web service que nous allons utiliser dans la suite.

Voici un exemple d’appel avec, comme critère de recherche, les noms de communes qui commencent par « toul »

https://datanova.laposte.fr/api/records/1.0/search/?dataset=laposte_hexasmal&q=toul

Pour information, il existe un autre site https://vicopo.selfbuild.fr qui fournit une API ne présentant pas l’inconvénient cité dans le site précédent. Sa forme est très simple: https://vicopo.selfbuild.fr/cherche/{critere}

Implémentation

C’est la version d’APEX 19.2 qui est utilisée dans ce qui suit. C’est à partir de celle-ci que l’on peut baser une LOV sur un web module.

Création d’un web module

On crée dans Oracle APEX un web module basé sur le endpoint https://datanova.laposte.fr/api/records/1.0/search/ et on ajoute deux paramètrex de type Query String variable :

  • dataset valeur fixe= laposte_hexasmal
  • q valeur dynamique

Important: On précise au niveau du second paramètre (q) que sa valeur sera préférentiellement utilisée pour une recherche. (il s’agit d’une nouveauté dans la version 19.2): Use for Row Search

L’impact de ce réglage est que APEX remplira automatiquement ce paramètre selon le contexte de la recherche: Si le web module est utilisé pour un report, alors ce qui est saisi dans la zone Search alimentera le paramètre q.
Si le web module est utilisé pour une LOV, alors ce qui est saisi dans le champs associé à cette LOV sera utilisé comme valeur de paramètre q.

Création d’une liste de valeurs multi-colonnes

On indique que la source de données de la nouvelle liste de valeurs est le web module créé précédemment.

A partir de la version 19.2, il est possible d’afficher plusieurs colonnes dans une popup LOV et on peut également injecter ces valeurs de colonnes dans plusieurs champs simultanément. Nous mettons à profit cette fonctionnalité pour alimenter à la fois le nom de la ville et la valeur du code postal.
Bien vérifier que les colonnes soient visibles dans le tableau des colonnes retournées par la LOV.

Prise en compte dans un formulaire

  • indiquer que le champs VILLE est basé sur une Popup Lov
  • Choisir la liste de valeur.
  • Préciser le nombre de caractère minimum à entrer avant de déclencher une recherche dans la LOV, autrement dit, avant de déclencher un appel du Web Module.
  • Designer les autres colonnes qui seront alimentées par la LOV.

L’alimentation du code postal se déclare en remplissant la propriété « champs supplémentaire » sous la forme de couples séparés par un point-virgule:

"colonne LOV: champs; colonne LOV: champs; ... "

Limitations

Si la LOV est de type Popup Lov, Il faut activer l’option Entrée manuelle. La conséquence que la popup se comportera comme un combo box. Sans cela, la lecture d’un enregistrement existant tombe en erreur. C’est dommage car cela oblige, d’un point de vue ergonomique, à cliquer sur la flèche vers le bas pour faire apparaître la liste de valeurs.

Remarque sur les certificats

Sur une instance APEX installée manuellement, il faut que le certificat du site appelé en REST soit enregistré dans le wallet de la database. Dans le cas contraire, on reçoit une erreur de ce type:

Remarque: Lors de la récupération du certificat, il a fallu que je choisisse la version du certificat encodé en Base64, sinon cela ne fonctionnait pas sur une database Oracle 18c express.

Annexes

Il est possible de récupérer le certificat directement sur le serveur avec une commande openssl, ce qui évite le recours à un navigateur. Cependant, je n’ai pas pu obtenir un format adapté au wallet Oracle. Peut-être s’agit il du format Base64 non respecté ?

openssl x509 -in cert.pem -text -noout

Un exemple complet d’implémentation d’Oracle Real Application Security avec Oracle APEX

Objectif

Objectif: établir une claire séparation des rôles entre l’équipe de développement et l’équipe chargée du modèle d’autorisations applicatives.

Plutôt que d’ajouter des filtres au niveau de l’application APEX ou bien au niveau de vues, l’approche est, ici, d’utiliser les fonctionnalités de Oracle RAS afin de mettre en place un contrôle d’accès via des rôles applicatifs.

Scénario

Une société fictive, BioAdvice, se prépare à lancer le Biogreen, un pesticide basé sur une nouvelle molécule.
Pour anticiper le volume à produire, l’équipe commerciale est chargée de sonder sa base installée afin d’enregistrer les intentions de commandes.
La force de vente de BioAdvice est organisée en cinq régions, avec un responsable régional à la tête et quelques commerciaux dans chacune des régions. Un directeur commercial supervise le niveau national et il est aidé par un assistant commercial. (voir Organisation en annexe).

Les règles d’accès et de visibilité sont les suivantes:

  • Le directeur commercial a accès sans restriction à toutes les informations.
  • L’assistant a accès à toutes les données, excepté la valeur du champs DISCOUNT
  • Le responsable régional a accès à toutes les données relative à la région dont il a la responsabilité.
  • Le commercial ne voit que les données, sans restriction, des clients qui sont dans son secteur.

Pour des raisons de commodité, un tableau excel a été envoyé à chaque commercial pour que celui-ci y enregistre les pré-commandes. L’idée est de consolider les tableaux qui seront remontés au fil de l’eau.

This image has an empty alt attribute; its file name is image-5-1024x576.png

Les tableaux sont remontés par mail ou déposés sur un file system puis assemblés via une macro excel.

Après quelques semaines de fonctionnement, cette approche s’avère inopérante car il est impossible d’obtenir une valeur consolidée en temps réel avec un bon niveau de fiabilité. On se retourne alors vers une solution basée sur un base de données unique avec un accès en ligne via une application web. La plateforme de développement retenue est Oracle APEX.

Une application est rapidement générée et l’échantillon ci-dessous est montré aux utilisateurs qui adhèrent à cette nouvelle interface.

La question qui reste à traiter concerne la visibilité des données. Comment respecter les règles qui ont été mentionnées plus haut ?
Lorsqu’on diffuse un tableau spécifique à chaque commercial, le problème est réglé par le fait même que le travail de découpage est déjà réalisé sous la forme de x tableaux spécifiques. Dans le cas d’une base centralisée, il faut ajouter une notion d’utilisateur et de droits d’accès. C’est ici qu’intervient Real Application Security (RAS).

Précisons que l’emploi de RAS n’est nullement un passage obligé ! On peut envisager une solution simple (mais présentant quelques défauts comme il sera discuté plus loin) consistant à créer une vue jouant le rôle de filtre.

Cette vue sera du type :

Rename ADO_PLAN to ADO_PLAN_T;
create or replace view ADO_PLAN as 
 select * from ADO_PLAN_T
 where region in (select region 
             from ado_emp 
             where job = 'responsable régional' and 
             login = lower(v('APP_USER'))
                 )
 or
 exists (select 1 
     from ado_emp 
     where login =lower(v('APP_USER')) and 
     job = 'directeur commercial'
     )
 or
 commercial = lower(v('APP_USER'))

On met à profit la connaissance de l’utilisateur qui est authentifié dans l’application APEX pour utiliser cette valeur (‘APP_USER’) dans le filtre de la vue. L’application reste inchangée. Il y a cependant une règle de sécurité qui devra être résolue en programmant directement une condition au niveau du champs DISCOUNT, dans la page liée au formulaire de détail ainsi que dans le rapport sur les commandes.

Génération d’une application APEX

Mise en oeuvre de RAS

Prise en compte de RAS dans l’interface APEX

Les utilisateurs doivent être enregistrés à la fois dans Oracle APEX, en tant qu’utilisateur et dans RAS en tant que principal. L’authentification sera toujours sous le contrôle d’APEX. A ce propos, un mécanisme de provisionnement est à mettre en place pour des architecture s’appuyant massivement sur RAS.

Masquage de colonnes pour certains rôles

RAS permet de masquer des colonnes. Dans notre exemple, les assistants commerciaux peuvent visualiser toutes les commandes, mais ne peuvent pas avoir connaissance du taux de discount accordé par le commercial pour son client. Observer que la colonne DISCOUNT est affichée avec des ‘xxx’.
On utilise la fonction:

COLUMN_AUTH_INDICATOR(col)
RETURN BOOLEAN;

Limitations, anomalies, bugs

Plugins
A ce stade, les tests que j’ai réalisés avec un plugin sont négatifs. C’est à dire que une requête sur la table DEMO.ADO_PLAN à l’intérieur d’un plugin ne ramène aucune ligne. Cela est bloquant dans l’usage d’un plugin de visualisation des données sur une maps Google que j’avais prévu d’utiliser.

Latence dans l’activation de RAS
Lors de l’activation de RAS, un délai de plusieurs heures peut s’avérer nécessaire pour que l’authentification fonctionne. Ci-dessous le message d’erreur obtenu. Quelques heures plus tard, ce message n’apparaissait plus.

Usage d’une subquery dans la définition du Data Policy
Je n’ai pas réussi a utiliser une sous-requête en voulant exprimer la règle concernant la région pour les responsables régionaux. Je pensais utiliser au départ une table telle qu’elle figure en annexe et invoquer le prédicat suivant:

'REGION IN (select REGION from DEMO.ADO_EMP where REGION = '||'REGION and -- login = lower(xs_sys_context(''xs$session'',''username'')))
dans la définition du REALM associé à l'ACL portant sur les responsables régionaux.
(à la place de 
       realms(2) := xs$realm_constraint_type(
                         realm => 'REGION = &' || 'PREGION'        
                    );
)

mais j’ai toujours abouti à des erreurs sur le prédicat. J’ai donc laissé tomber cette piste et j’ai créé autant d’ACLs qu’il y avait de régions. D’un point de vue « philosophique » c’est peut-être la meilleure approche dans la mesure où les informations sur l’organisation sont déportées dans le dictionnaire de RAS et non pas dans une table (ADO_EMP) d’un schéma applicatif.

Annexes

Scripts de création des Principals, ACLs et Data Security Policies

Tous les scripts de création d’un modèle d’autorisation, ci-dessous, sont accessibles dans leur dernière version sur github.

Principals

exec xs_principal.create_role(name => 'sr_role', enabled => true);
exec xs_principal.create_role(name => 'hq_role', enabled => true);
exec xs_principal.create_role(name => 'sa_role', enabled => true);
exec sys.xs_principal.create_role(name => 'sud_ventes', enabled => TRUE);
exec sys.xs_principal.create_role(name => 'nord_ventes', enabled => TRUE);
exec sys.xs_principal.create_role(name => 'est_ventes', enabled => TRUE);
exec sys.xs_principal.create_role(name => 'ouest_ventes', enabled => TRUE);
exec sys.xs_principal.create_role(name => 'centre_ventes', enabled => TRUE);
 grant db_bio to sr_role;
 grant db_bio to hq_role;
 grant db_bio to sa_role;
 grant db_bio to ouest_ventes;
 grant db_bio to sud_ventes;
 grant db_bio to nord_ventes;
 grant db_bio to est_ventes;
 grant db_bio to centre_ventes;
 -- Directeur ventes
 exec  xs_principal.create_user(name => 'alain', schema => 'DEMO');
 exec  sys.xs_principal.set_password('alain', 'alain');
 exec  xs_principal.grant_roles('alain', 'hq_role');
 -- Responsable régional
 exec  xs_principal.create_user(name => 'ygor', schema => 'DEMO');
 exec  sys.xs_principal.set_password('ygor', 'ygor');
 exec  xs_principal.grant_roles('ygor', 'ouest_ventes');
 -- Commercial
 exec  xs_principal.create_user(name => 'brice', schema => 'DEMO');
 exec  sys.xs_principal.set_password('brice', 'brice');
 exec  xs_principal.grant_roles('brice', 'sr_role');
 -- Assistant commercial
 exec  xs_principal.create_user(name => 'pierre', schema => 'DEMO');
 exec  sys.xs_principal.set_password('pierre', 'pierre');
 exec  xs_principal.grant_roles('pierre', 'sa_role');
 
 Give create session privilege
 BEGIN  
     SYS.XS_PRINCIPAL.GRANT_ROLES('ALAIN', 'XSCONNECT'); 
     SYS.XS_PRINCIPAL.GRANT_ROLES('YGOR', 'XSCONNECT'); 
     SYS.XS_PRINCIPAL.GRANT_ROLES('BRICE', 'XSCONNECT');
     SYS.XS_PRINCIPAL.GRANT_ROLES('PIERRE', 'XSCONNECT');
 END;
 /

ACLs

 Creating ACLs: EMP_ACL, IT_ACL, and HR_ACL
 (Ace stands for Access Control Entry)
 declare
       aces xs$ace_list := xs$ace_list();
     begin
       aces.extend(1);
 -- SR_ACL: This ACL grants SR_ROLE the privileges to view a sales rep's
 --          own record including DISCOUNT column.
 aces(1) := xs$ace_type(
             privilege_list => xs$name_list('select','view_discount'),
             principal_name => 'sr_role');
sys.xs_acl.create_acl(name => 'sr_acl', 
ace_list  => aces,
sec_class => 'bioprivs');
 -- HQ_ACL:  This ACL grants HQ_ROLE the privileges to view and update all
 -- order records including DISCOUNT column.
 aces(1):= xs$ace_type(
          privilege_list => xs$name_list('all'),
          principal_name => 'hq_role');
sys.xs_acl.create_acl(name => 'hq_acl',
ace_list  => aces,
sec_class => 'bioprivs');
 -- SA_ACL:  This ACL grants SA_ROLE the privileges to view and update all
 -- order records excluding DISCOUNT column.
      aces(1):= xs$ace_type(
          privilege_list => xs$name_list('select'),
          principal_name => 'sa_role');
 sys.xs_acl.create_acl(name => 'sa_acl',
ace_list  => aces,
sec_class => 'bioprivs');    
 end;
    /

Data Security Policy

 --Example 5-19 Creating the EMPLOYEES_DS Data Security Policy
 declare
    realms   xs$realm_constraint_list := xs$realm_constraint_list();
    cols     xs$column_constraint_list := xs$column_constraint_list();
 begin
    realms.extend(4);
 -- Realm #1: Only the order's own record.   
 --  SR_ROLE can view the realm including DISCOUNT column. 
  
    realms(1) := xs$realm_constraint_type(realm => 'COMMERCIAL = lower(xs_sys_context(''xs$session'',''username''))',
 acl_list => xs$name_list('sr_acl'));  

 -- Realm #2: The records in the same region as the Region Manager.  --                 realms(2) := xs$realm_constraint_type(realm => 'REGION = &' || 'PREGION' );
  
 -- Realm #3: All the records.  
 -- HQ_ROLE can view and update the realm including DISCOUNT column.  realms(3) := xs$realm_constraint_type(realm => '1 = 1',
   acl_list => xs$name_list('hq_acl'));

  --  Realm #4R: All the records and no acces to DISCOUNT.
  --  SA_ROLE can view and update the realm excluding DISCOUNT column.
   realms(4) := xs$realm_constraint_type(
        realm    => '1 = 1',
        acl_list => xs$name_list('sa_acl'));
-- Column constraint protects DISCOUNT column by requiring view_discount  -- privilege.  
 cols.extend(1);
 cols(1) := xs$column_constraint_type(column_list => xs$list('DISCOUNT'),
     privilege   => 'view_discount');
sys.xs_data_security.create_policy(name=> 'bio_ds', 
  realm_constraint_list  => realms,
  column_constraint_list => cols);  sys.xs_data_security.create_acl_parameter(policy => 'bio_ds',
  parameter => 'PREGION', 
  param_type => XS_ACL.TYPE_VARCHAR);  
end;
/

ACL pour une région

DECLARE
   ace_list XS$ACE_LIST;
 BEGIN
   ace_list := XS$ACE_LIST(
              XS$ACE_TYPE(privilege_list => XS$NAME_LIST('SELECT'),
                      granted => true,
                      principal_name => 'ouest_ventes'),
              XS$ACE_TYPE(privilege_list => XS$NAME_LIST('SELECT', 'view_discount'),
            granted => true,
           principal_name => 'ouest_ventes'));
 sys.xs_acl.create_acl(name => 'view_ouest_ventes',
                 ace_list => ace_list,
                 sec_class => 'bioprivs',
                 description => 'Authorize read access for the ouest region');
 sys.xs_acl.add_acl_parameter(acl => 'view_ouest_ventes',
                            policy => 'bio_ds',
                            parameter => 'PREGION',
                            value => 'ouest');
 END;
 /  
 -- ACLs for other regions have to be done !

Validation

-- Validating policy
 begin
   if (sys.xs_diag.validate_workspace()) then
     dbms_output.put_line('All configurations are correct.');
   else
     dbms_output.put_line('Some configurations are incorrect.');
   end if;
 end;
 /

Activation

-- Apply the data security policy to the EMPLOYEES table.
 begin
   xs_data_security.apply_object_policy(
     policy => 'bio_ds', 
     schema => 'DEMO',
     object =>'ADO_PLAN');
 end;
 /

Jeu de test

Organisation

Le tableau ci-dessous récapitule les Business Roles dans l’organisation de la société BioAdvice. Chacun sera traduit en un ou plusieurs rôles applicatifs. Les noms en gras sont ceux échantillonnés pour la démo

NOMPRENOMLOGINREGIONJOB
KlaessonFilmorefilmorecentreassistant commercial
GinnetyAdrienadriencentreresponsable régional
CostesAlainalaincorpdirecteur commercial
GrinishTaitetaitecorpdirecteur marketing
EarlyMerrickmerrickestcommercial
Mc PakeWaverleywaverleyestcommercial
GodingCharlescharlesestresponsable régional
KettelBrennenbrennennordcommercial
IannoAdamadamnordresponsable régional
PetcheyCorycorynordcommercial
CreanyIvarivarouestcommercial
CalkinYgorygorouestresponsable régional
TchaikovPatrickpatrickouestcommercial
SpondleyTalberttalbertouestcommercial
DuguetPierrepierreouestassistant commercial
LacombesBricebriceouestcommercial
FattoreEveretteverettsudcommercial
RandallCleoncleonsudresponsable régional
StedallGarrotgarrotsudcommercial

Feuille excel des commandes consolidées

Ce tableau est celui utilisé pour l’initialisation des données dans la table ADO_PLAN. Il est le résultat de la consolidation de tous les tableaux partiels remontés par les commerciaux.

Access to MyDeezer

This application gives ability to view and export playlists content in CSV/HTML format from your own Deezer account.
In order to export a playlist content, select it in the playlists list, choose the output format (CSV or HTML) then click on Export button.
Limitations: the maximum number of tracks beeing exported is 1000.
During navigation through tracks, it’s possible to set the number of tracks displayed on each page. Default is 10.

It’s possible to group tracks by artist : instead « 1.Primary report », Select the « 2.By artist » report in the upper select list.

Samples catalog

If choosing the HTML format, the tool will extract all the tracks in one html document with a link to a title preview (similar to the Deezer behaviour when the recipient has’nt got a deezer account). When the CSV format is chosen, the preview link is not retained in the exported attributes.

Issues

if the message « No data found » is displayed, that means probably that the token has expired. In this case, login again.
The Access token value can be seen in the Infos entry menu.

Consent and Confidentiality

The tool requires a very basic authorization set and has knowledge of « account name » only, no more. The consent page is displayed after first login page.

Updated on 28 oct 2019
GPM Factory

Leverage the Oracle BI Publisher templating power and produce high fidelity reports

Abstract

Oracle APEX is a tool which is well suited for rapid applications developpement (RAD). It’s possible to produce a tabular report based one a query in a very fast way. This is a standard feature. But when the goal is to build high precision reports « pixel perfect », like an invoice, for instance, we must switch to an other approach. The good news is that Oracle APEX gives ability to use XSLT-FO templates instead the standard ones. Perfect ! But how authoring these XSLT-FO templates, while keeping the APEX low code philosophy ? The following in this post explains a possible solution.

The approach is to use Oracle BI PUblisher Desktop product (BIP). It’s a plugin for Microsft Word which « transforms » MS Word in a template authoring tool without burden of with XSLT and XPATH syntax. ( One other alternative is to use Apache POI project then adding XPATH expressions in the XSL-FO template)
Important notice: It will be possible to use a functionnal subset of the BIP product but not all features. Therefore, the following informations must be considered as an experimental work only.

Materials & Documentation

Prerequisites

License Prerequisites

  • A commercial license for using BI Publisher (BIP)
  • A open source license for the SAXON xsl parser

We’ll not rely on BIP FOP engine and we’ll just use the BIP Desktop plugin. The license for BIP can be purchased on a Named User metric. That means that we can manage to buy at a low price but with a minimum of 10 licenses per processor.

SAXON is sold through several editions. The home edition, SAXON-HE is an open source product under Mozilla public license V2. It can be enough for testing and regular use as well.

Download & Documentation

Documentation

About XPATH and XSLT Syntax, BIP Desktop samples are a good source of inspiration:
« C:\Program Files\Oracle\BI Publisher\BI Publisher Desktop\Template Builder for Word\samples\RTF templates »

Technical prerequisites

This post assumes a basic knowledge of RTF template design with BIP.
Once the RTF template is setup, we use the embeded function for exporting the template as an XSL-FO stylesheet.

Export option inside BIP Desktop plugin

From BIP plugin, an export menu option gives ability to export the template as a XSL-FO stylesheet. This exported stylesheet contains additional xsl function which are specific to BIP and can’t be used directly in Oracle APEX. In order to use a regular FO Processor (the one imbedded in APEX), we have to either remove or convert it, when possible.
For that purpose, we use a simple plsql procedure which will do a kind of cleanup in the template. It’is a fork of a Pavel Glebov’s project https://github.com/patrickmonaco/BIP_TO_FOP.
Some additional details have been added.
The XSL-FO beeing a regular XML document,it would have been ideally more judicious to use a XSLT parsing method, but this discussion is out of the scope of this post.

Implementation

Reminders about the FOP engine

An Apache FOP engine 2.3 is embedded in the ORDS java application. This engine relies on a XALAN (XSLTC) transformerFactory. This parser is compliant whit XSLT V1 but not XSLT v2. And this is a little bit ennoying for our purpose because some group functions generated by BIP should have to be rewritten for working in XSLT v1. Because we don’t want to developp a too complex convert tool, I prefered susbitute for XALAN (XSLTC) an other XSLT parser (SAXON) in order to minimize conversion effort.

This parser, SAXON, is XSLT v2 compliant . Keep in mind that license fees can be required in some cases.

So, once we setup the new transformerFactory (new way to transform XML input) in the CATALINA environmement file (if Apache Tomcat is used as a java container) , and after adding SAXON library in Tomcat lib directory, the way becomes much easier.

set CATALINA_OPTS=%CATALINA_OPTS% -Djavax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl

Once the query has been created and the layout has been associated to, the report can be launched with the following URL:
f?p=&APP_ID.:0:&SESSION.:PRINT_REPORT=empdept

Install database objects

A DDL script (FOP_DDL) and a package, BOP_PKG, have to be installed in the worskpace owner schema.
cf https://github.com/patrickmonaco/BIP_TO_FOP

Adding a template repository

A simple APEX application is provided for managing templates. When a new template BIP template is uploaded, a new converted template is created.
At this stage, the lifecycle is:

  • Update/Create RTF template with MS Word & BIP Plugin
  • Export RTF template in XSL-FO (with BIP Word Plugin)
  • Upload the new version template in BOP repository
  • Download the converted template
  • Remove the previous layout
  • Re-create a new layout with the same name (or not) and upload the converted template
  • Update the report query to setup the new layout

The process is quite cumbersome because on each update, we have to remove then re-create a new layout, then upload the converted wslt-fo template. In order to minimize the tasks, the idea is to synchronise the repository with the apex layouts internal table.
Note: The following step is optionnal. Remove the related code inside the application if this step is to be ignored.

The name of this table is: WWV_FLOW_REPORT_LAYOUTS
The pl/sql procedure then updates the layout by uploading the template converted previously. The APEX table is: WWV_FLOW_REPORT_LAYOUTS .
Full access rights on this table must be granted to the workspace owner.

As SYS user, let’s grant DML on this table

grant ALL on APEX_<XXX>.WWV_FLOW_REPORT_LAYOUTS TO DEMO;

Now the lifecycle becomes:

  • Update RTF template with MS Word
  • Export RTF template in XSL-FO (with BIP Word Plugin)
  • Upload the new version template in BOP repository

Because each update in BOP Table will update the conterpart in WWV_FLOW_REPORT_LAYOUTS table, no more effort is needed.

Limitations:

  • Numerous BIP (xdo) functions are not supported
  • Wordarts, shapes are not supported
  • Static graphics (ie: an image included in the rtf template) encoded in base64 are not supported
  • Formatting in XPATH expression is supported for number datatype only

Discussion about XML dataset

Above, this is a XML sample generated by a Report Query.

select emp.*, 
dept.dname, 
dept.loc 
from emp, dept 
where emp.deptno = dept.deptno 

We can observe that the dataset is rendered as a single table (a single rowset), in other words, a flat tree. There is no way to build, with a regular report query, a complex tree document. The consequence is that we have to use Group Function inside the xst-fo template if break page is needed in the final report.

Therefore, because of the previous point, we’ll have to rely on XSLT group function in the plugin, for achieving some behaviours like page break. This point explains why we subsitute to XALAN (XSLTC), the SAXON parser which is more suitable.

Use cases

Tabular reports

There is no difficulty to design a template which produce a tabular report. It’s possible to leverage most of the features of BIP Desktop combined with the powerful of MS Word. The page break is managed by the Apache XSL-FO engine.

Tabular output with conditionnal formating

Invoices

It’s a very common type of document where there is :

  • a header: (invoice header, customer address, ..)
  • one or more set of data multi-lines related to the invoice (product items)
  • a footer: total and legal mentions, terms and conditions …

In the context of Oracle APEX, we must keep in mind that the XML output is flat. We just can leverage multiple rowsets in the same report. That leads to two different ways for building the XML dataset. The first one is to get a single rowset by joing all needed data upon a common key (order_id). The second one is to get a first rowset for the header information and other ones for multiple lines data.

One occurence of a result set

Goal: print a single document with a single occurence. ie: An invoice for a given order id.

In this case, the query can contains a single sql statement whichs is a join with all participants tables (order, ords items, products, customer).
Or we can combine two queries: one for the header ans on another for the order lines.

All occurences of a result set

In this case, the goal is to print a single document which contains all occurences returned by a query with an optional page break between occurences.

Query:

select emp.*, 
dept.dname, 
dept.loc 
from emp, dept 
where emp.deptno = dept.deptno 

Oracle APEX generates a XML document with the following schema:

XML schema generated by APEX Report Query
Output pdf with page break on Department
BIP RTF template



Fonts

Using specific fonts requires some additionnal setup. We need to adapt ORDS starting parameters.
A parameter in ORDS configuration file must be modified for pointing to the FOP configuration file:

<entry key="fop.configfile">
X:\distrib\fop-2.2\fop\conf\fopapex.xconf
</entry>

Once the server has been restarted, all the requierd fonts will be honored by Apache FOP. Otherwise, there is a fallback mechanism which maps the original fonts on a small subset (Times Roman).

Fonts embedded by the pdf document with fonts
Fonts embedded in the output pdf without fonts

Conclusion

The method presented previously shows up a good balance between complexity/High quality and simplicity/basic rendition in pdf document design. The BIP desktop plugin gives a very good flexibility provided you get a correct knowledge of MS Word.

APPENDIX

The following sample gives an overview of the XPATH query wich is generated by the BIP desktop plugin. Of course, it’s always possible to enrich the behavior by adding some additional XPATH expressions.

XPATH generated by BIP Desktop plugin
PDF output with the FOP engine / report with XSLT break
PDF output with the APEX FOP engine – Master detail
Simple Invoice RTF template in BIP desktop

Queries used for invoice demo

Multi-queries – two rowsets

select 
o.order_id,
o.customer_id,
o.order_total,
to_char(o.order_timestamp,'DD MONTH YYYY') order_timestamp,
c.cust_first_name,
c.cust_last_name,
c.cust_street_address1,
c.cust_street_address2,
c.cust_city,
c.cust_state,
c.cust_postal_code
from demo_orders o,
demo_customers c
where c.customer_id = o.customer_id
and o.order_id = 1

select
i.unit_price,
i.quantity,
round((i.quantity * i.unit_price),2) amount,
p.product_name,
p.product_description
from
demo_order_items i,
demo_product_info p
where p.product_id = i.product_id and
i.order_id=1
Sample multiple queries (rowsets)

Single Query – single rowset

select 
 o.order_id, 
 o.customer_id,
 o.order_total,
 o.order_timestamp,
 i.unit_price,
 i.quantity,
 p.product_name,
 p.product_description,
 c.cust_first_name,
 c.cust_last_name,
 c.cust_street_address1,
 c.cust_street_address2,
 c.cust_city,
 c.cust_state,
 c.cust_postal_code
 from demo_orders o,
 demo_order_items i,
 demo_customers c,
 demo_product_info p
 where o.order_id = i.order_id and
 p.product_id = i.product_id and
 c.customer_id = o.customer_id