How to install Polr Admin in Symfony with the Sonata Admin bundle

The news aggregator Africa News Hub uses two instances of URL shortener, one for outgoing links to referenced sites, and another for incoming links included in messages posted on social networks. Polr, which defines itself as a modern and robust URL shortener, is used on both instances.

By default, having two Polr instances means there are two backends to manage, one for each intance. The Polr Admin package was created to make it simple to deal with multiple Polr instances.

Polr Admin is a package that allows you to manage multiple Polr instances from a single location. It is not a standalone application, but an extension to be installed on a running PHP application. The authentication and authorization functions are left to the application, and thus will not be discussed in this article.

The Polr Admin interface is displayed in a single web page, and uses Ajax to update its content. The Ajax function is implemented with the Jaxon library, which converts PHP classes into Javascript classes, so that they can be called directly from the browser.

This article describes the installation of Polr Admin with the Sonata Admin bundle, a package that allows you to create backends for the Symfony framework. For Africa News Hub, Polr Admin is integrated into a Laravel backend. Symfony was chosen for this article to show how, thanks to the Jaxon library, Polr Admin can be installed on other PHP frameworks.

The Polr Restful API

The Polr Admin package interacts with the Polr instances that it manages using the Polr API package, which wraps the Polr functions into a Restful API. It is therefore required to install Polr API on each instance of Polr to manage.

The Polr API package installs with Composer.

composer require lagdo/polr-api ~0.1

Then, you have to declare the package in the `bootstrap/app.php` file.

$app->register(\Lagdo\Polr\Api\PolrApiServiceProvider::class);

Once the Polr API package is installed, you must create an API key for a user on each instance of Polr. From here, it is possible to get access to Polr with an API testing tool like Postman.

We will now install Polr Admin on a different host. The next two sections describe how to install Symfony and the Sonata Admin bundle.

Symfony installation

We will begin by installing Symfony version 3.3, following the instructions given at https://symfony.com/doc/3.3/setup.html.

curl -LsS https://symfony.com/installer -o ~/bin/symfony
chmod a+x ~/bin/symfony
symfony new polr-admin 3.3

When the installation is complete, move into the Symfony directory and start the embedded web server.

cd polr-admin
php bin/console server:run

By browsing the address at http://localhost:8000/, you can see the above page, showing that the installation was successful.

Symfony Home
Symfony Home

The Sonata Admin bundle installation

The Sonata Admin bundle installs with Composer.

composer require sonata-project/admin-bundle

When the installation is complete, edit the app/AppKernel.php file to declare the Sonata bundles.

// ...
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = [
            // ...

            // These are the bundles the SonataAdminBundle relies on
            new Sonata\CoreBundle\SonataCoreBundle(),
            new Sonata\BlockBundle\SonataBlockBundle(),
            new Knp\Bundle\MenuBundle\KnpMenuBundle(),

            // And finally, the storage and SonataAdminBundle
            new Sonata\AdminBundle\SonataAdminBundle(),
        ];

        // ...
    }

    // ...
}

Then, configure the translations and the Sonata bundle in the app/config/config.yml file.

framework:
    translator: { fallbacks: [en] }

# ...

sonata_block:
    default_contexts: [cms]
    blocks:
        # enable the SonataAdminBundle block
        sonata.admin.block.admin_list:
            contexts: [admin]

Now add the Sonata bundle routes in the app/config/routing.yml file.

admin_area:
    resource: "@SonataAdminBundle/Resources/config/routing/sonata_admin.xml"
    prefix: /admin

Finally, run the following commands.

$ php bin/console cache:clear
$ php bin/console assets:install

The Sonata Admin bundle is now installed. The link http://localhost:8000/admin shows its homepage, which is empty for now.

Sonata Admin Home
Sonata Admin Home

Jaxon and Polr Admin installation

The jaxon-php/jaxon-symfony package that provides the Jaxon functions in a Symfony application can now be installed.
The views of the Polr Admin package use the Blade template engine, which requires the jaxon-php/jaxon-blade package to be installed.

$ composer require jaxon-php/jaxon-symfony ~2.0
$ composer require jaxon-php/jaxon-blade
$ composer require lagdo/polr-admin

When the installation is complete, declare the Jaxon bundle in the app/AppKernel.php file.

$bundles = array(
    ...
    new Jaxon\AjaxBundle\JaxonAjaxBundle(),
);

Configure the Jaxon bundle in the app/config/config.yml file.

imports:
    ...
    - { resource: jaxon.yml }
    - { resource: "@JaxonAjaxBundle/Resources/config/services.yml" }

Create the app/config/jaxon.yml file with the following content. It’s the Jaxon configuration.

parameters:
jaxon:
    app:
    lib:
        core:
            request:
                uri:     "ajax"
            prefix:
                class:   ""
            js:
                lib:
                app:
                extern:  false
                minify: false
            dialogs:
                default:
                    modal: bootbox
                    alert: noty
                    confirm: noty

In this configuration, the jaxon.lib.core.request.uri parameter indicates that Ajax requests will be sent to the URL /ajax. The parameters in the jaxon.lib.core.js section indicate that Jaxon’s Javascript code will be inserted directly into the web page, and the parameters in the jaxon.lib.dialogs section indicate that the modal windows will use the Bootbox library, while Alert and confirmation messages will use the Noty library. The corresponding files will be automatically loaded into the page by Jaxon.

Then define the following two routes in the app/config/routing.yml file.

polr_index:
    path: /polr
    defaults: { _controller: AppBundle:Polr:index }

polr_jaxon:
    path: /ajax
    defaults: { _controller: AppBundle:Polr:jaxon }
    methods: [POST]

The first route is linked to the PolrController::indexAction() function, which prints the dashboard home page. The second one is linked to the PolrController::jaxonAction() function, which will process Jaxon’s Ajax requests.

This is the controller code, to be saved in the src/AppBundle/Controller/PolrController.php file.

<?php

namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class PolrController extends Controller
{
    private $jaxon;
    private $polr;

    protected function getJaxon()
    {
        $this->jaxon = $this->get('jaxon.ajax');
        $this->polr = $this->jaxon->package('polr.admin');
        $this->polr->config($this->get('kernel')->getProjectDir() . '/app/config/polradmin.yml');
    }

    /**
     * Show the Polr Admin page.
     *
     * @return void
     */
    public function indexAction()
    {
        $this->getJaxon();
        $this->jaxon->register();
        return $this->render('polr.html.twig', [
            'jaxon' => $this->jaxon,
            'polr' => $this->polr,
            'pageTitle' => "Symfony Framework",
        ]);
     }

    /**
     * Process a Jaxon request.
     *
     * @return void
     */
    public function jaxonAction()
    {
        $this->getJaxon();
        // Process the Jaxon request
        if($this->jaxon->canProcessRequest())
        {
            $this->jaxon->processRequest();
        }
    }
}

The PolrController::indexAction() function prints the app/Resources/views/polr.html.twig template, whose content follows.

{% extends '@SonataAdmin/standard_layout.html.twig' %}

{% block stylesheets %}
{{ parent() }}

{{ jaxon.css()|raw }}
{{ polr.css()|raw }}
{% endblock %}

{% block javascripts %}
{{ parent() }}

{{ jaxon.js()|raw }}
{{ jaxon.script()|raw }}
{{ polr.js()|raw }}

$(document).ready(function() {
    {{ polr.ready()|raw }}
});

{% endblock %}

{% block page_title 'Welcome to the PolrAdmin Demo application' %}

{% block content_title 'Polr Admin Dashboard' %}

{% block body_id 'polr-admin' %}

{% block body_class 'list' %}

{% block sonata_admin_content %}
{{ parent() }}

{{ polr.html()|raw }}

{% endblock %}

The Jaxon and Polr Admin packages are now installed.
The last step is to declare the instances of Polr to manage. This is done in the app/config/polradmin.yml file, as in the following example.

default: first
endpoints:
    first:
        name: First Polr Instance
        url: http://first.domain.tld
        api: api/v2
        key: 012365478978965412300321456987
    second:
        name: Second Polr Instance
        url: http://second.domain.tld
        api: api/v2
        key: 987456321003214569879874563210

In this configuration, the default parameter indicates the instance on which Polr Admin will connect by default, and the endpoints section contains the list of instances.

The Polr Admin is now installed and configured, and can be accessed at http://localhost:8000/polr. Here’s the homepage.

The Settings tab displays a drop-down list where the user can choose the current Polr instance.

The other tabs display the data of the current instance: the user’s list of links, the list of all links, the list of users, and the link access statistics.

The user links
All the llinks
All the llinks
The users list
The users list
Statistics
Statistics

The code for this article is online at this address: https://github.com/lagdo/polr-admin-sonata.

Installer Polr Admin dans Symfony avec le bundle Sonata Admin

L’agrégateur de sites d’information Africa News Hub utilise deux instances de raccourcisseur d’URL, une pour les liens sortants vers les sites référencés, et une autre pour les liens entrants contenus dans les messages postés sur les réseaux sociaux. Polr, qui se définit comme un raccourcisseur d’URL moderne et robuste, est utilisé sur les deux instances.

Par défaut, avoir deux instances de Polr implique de gérer deux backends séparés, un sur chaque instance. Le package Polr Admin a été créé pour unifier et simplifier la gestion d’un ensemble d’instances de Polr.

Polr Admin est un package qui permet d’administrer une ou plusieurs instances de Polr. Ce n’est pas une application indépendante, mais plutôt une extension à installer dans une application PHP existante. Les fonctions d’authentification sont laissées à la charge de l’application qui l’héberge, et nous n’en parlerons pas dans cet article.

L’interface de Polr Admin s’affiche dans une seule page web, et utilise Ajax pour mettre à jour son contenu. La fonction Ajax est mise en oeuvre avec la librarie Jaxon, qui convertit des classes PHP en classes Javascript, de façon à pouvoir les appeler directement dans le navigateur.

Cet article article décrit l’installation de Polr Admin avec le bundle Sonata Admin, un package qui permet de créer des backends pour le framework Symfony.
Pour Africa News Hub, Polr Admin est intégré dans un backend Laravel. Le choix de Symfony pour cet article a pour but de montrer comment grâce à la librairie Jaxon, Polr Admin peut s’installer sur d’autres frameworks PHP.

Polr Restful API

Le package Polr Admin interagit avec les instances de Polr qu’il administre à l’aide du package Polr API, qui présente les fonctions de Polr dans une API Restful.
Il faut donc installer Polr API sur chaque instance de Polr à administrer.

Le package Polr API s’installe avec Composer.

$ composer require lagdo/polr-api ~0.1

Ensuite, il faut déclarer le package dans le fichier bootstrap/app.php.

$app->register(\Lagdo\Polr\Api\PolrApiServiceProvider::class);

Une fois le package Polr API installé, il faut créer une clé d’API pour un utilisateur sur chaque instance de Polr.
A partir d’ici, il est possible de tester l’accès à l’API avec un outil comme Postman.

Pour la suite, nous allons installer Polr Admin sur un hôte différent. Les deux sections suivantes décrivent l’installation de Symfony et du bundle Sonata Admin.

Installer Symfony

Pour commencer, nous allons installer la version 3.3 de Symfony, en suivant les instructions données sur la page https://symfony.com/doc/3.3/setup.html.

$ curl -LsS https://symfony.com/installer -o ~/bin/symfony
$ chmod a+x ~/bin/symfony
$ symfony new polr-admin 3.3

Une fois l’installation terminée, se déplacer dans le répertoire de Symfony et démarrer le serveur web intégré.

$ cd polr-admin
$ php bin/console server:run

En accédant avec un navigateur à l’adresse http://localhost:8000/, on peut voir la page suivante, qui montre que l’installation s’est bien déroulée.

Accueil Symfony
Accueil Symfony

Installer Sonata Admin Bundle

Le bundle Sonata Admin s’installe avec Composer.

$ composer require sonata-project/admin-bundle

Une fois qu’il est installé, éditer le fichier app/AppKernel.php pour déclarer les bundles de Sonata.

// ...
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = [
            // ...

            // These are the bundles the SonataAdminBundle relies on
            new Sonata\CoreBundle\SonataCoreBundle(),
            new Sonata\BlockBundle\SonataBlockBundle(),
            new Knp\Bundle\MenuBundle\KnpMenuBundle(),

            // And finally, the storage and SonataAdminBundle
            new Sonata\AdminBundle\SonataAdminBundle(),
        ];

        // ...
    }

    // ...
}

Ensuite, dans le fichier app/config/config.yml, configurer les translations et le bundle Sonata.

framework:
    translator: { fallbacks: [en] }

    # ...

sonata_block:
    default_contexts: [cms]
    blocks:
        # enable the SonataAdminBundle block
        sonata.admin.block.admin_list:
        contexts: [admin]

Enfin, dans le fichier app/config/routing.yml, ajouter les routes du bundle Sonata.

admin_area:
    resource: "@SonataAdminBundle/Resources/config/routing/sonata_admin.xml"
    prefix: /admin

Pour terminer l’installation, exécuter les commandes suivantes.

$ php bin/console cache:clear
$ php bin/console assets:install

Le bundle Sonata Admin est maintenant installé, et le lien http://localhost:8000/admin affiche sa page d’accueil, qui est vide pour l’instant.

Accueil Sonata Admin
Accueil Sonata Admin

Installation de Jaxon et Polr Admin

Le package jaxon-php/jaxon-symfony qui fournit les fonctions de Jaxon dans une application Symfony, doit d’abord être installé.
Les vues du package Polr Admin utilisent le moteur de template Blade, ce qui nécessite l’installation du package jaxon-php/jaxon-blade.

$ composer require jaxon-php/jaxon-symfony ~2.0
$ composer require jaxon-php/jaxon-blade
$ composer require lagdo/polr-admin

Une fois l’installation terminée, déclarer le bundle Jaxon dans le fichier app/AppKernel.php.

$bundles = array(
    ...
    new Jaxon\AjaxBundle\JaxonAjaxBundle(),
);

Configurer le bundle Jaxon dans le fichier app/config/config.yml.

imports:
    ...
    - { resource: jaxon.yml }
    - { resource: "@JaxonAjaxBundle/Resources/config/services.yml" }

Pour configurer la librairie Jaxon, créer le fichier app/config/jaxon.yml avec le contenu suivant.

parameters:
jaxon:
    app:
    lib:
        core:
            request:
                uri:     "ajax"
            prefix:
                class:   ""
            js:
                lib:
                app:
                extern:  false
                minify: false
            dialogs:
                default:
                    modal: bootbox
                    alert: noty
                    confirm: noty

Dans cette configuration, le paramètre jaxon.lib.core.request.uri indique que les requêtes Ajax vont être envoyées sur l’URL /ajax. Les paramètres de la section jaxon.lib.core.js indiquent que le code Javascript de Jaxon va être inséré directement dans la page web, et les paramètres de la section jaxon.lib.dialogs indiquent que les fenêtres modales utiliseront la librairie Bootbox, tandis que les messages d’alerte et de confirmation utiliseront la librarie Noty. Les fichiers correspondant seront automatiquement chargés dans la page par Jaxon.

Dans le fichier app/config/routing.yml, définir les deux routes ci-dessous.

polr_index:
    path: /polr
    defaults: { _controller: AppBundle:Polr:index }

polr_jaxon:
    path: /ajax
    defaults: { _controller: AppBundle:Polr:jaxon }
    methods: [POST]

La première est liée à la fonction PolrController::indexAction(), qui va afficher la page de Polr Admin. La seconde est liée à la fonction PolrController::jaxonAction(), qui va traiter les requêtes Ajax de Jaxon.

Voici le code correspondant, à placer dans le fichier src/AppBundle/Controller/PolrController.php.

<?php

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class PolrController extends Controller
{
    private $jaxon;
    private $polr;

    protected function getJaxon()
    {
        $this->jaxon = $this->get('jaxon.ajax');
        $this->polr = $this->jaxon->package('polr.admin');
        $this->polr->config($this->get('kernel')->getProjectDir() . '/app/config/polradmin.yml');
    }

    /**
     * Show the Polr Admin page.
     *
     * @return void
     */
    public function indexAction()
    {
        $this->getJaxon();
        $this->jaxon->register();
        return $this->render('polr.html.twig', [
            'jaxon' => $this->jaxon,
            'polr' => $this->polr,
            'pageTitle' => "Symfony Framework",
        ]);
     }

    /**
     * Process a Jaxon request.
     *
     * @return void
     */
    public function jaxonAction()
    {
        $this->getJaxon();
        // Process the Jaxon request
        if($this->jaxon->canProcessRequest())
        {
            $this->jaxon->processRequest();
        }
    }
}

La fonction PolrController::indexAction() affiche le template app/Resources/views/polr.html.twig dont voici le contenu.

{% extends '@SonataAdmin/standard_layout.html.twig' %}

{% block stylesheets %}
{{ parent() }}

{{ jaxon.css()|raw }}
{{ polr.css()|raw }}
{% endblock %}

{% block javascripts %}
{{ parent() }}

{{ jaxon.js()|raw }}
{{ jaxon.script()|raw }}
{{ polr.js()|raw }}

$(document).ready(function() {
    {{ polr.ready()|raw }}
});

{% endblock %}

{% block page_title 'Welcome to the PolrAdmin Demo application' %}

{% block content_title 'Polr Admin Dashboard' %}

{% block body_id 'polr-admin' %}

{% block body_class 'list' %}

{% block sonata_admin_content %}
{{ parent() }}

{{ polr.html()|raw }}

{% endblock %}

La dernière étape consiste à déclarer les instances de Polr à administrer. Cela se fait dans le fichier app/config/polradmin.yml, dont voici un exemple.

default: first
endpoints:
    first:
        name: First Polr Instance
        url: http://first.domain.tld
        api: api/v2
        key: 012365478978965412300321456987
    second:
        name: Second Polr Instance
        url: http://second.domain.tld
        api: api/v2
        key: 987456321003214569879874563210

Dans cette configuration, le paramètre default désigne l’instance sur laquelle Polr Admin va se connecter par défaut, et la section endpoints contient la liste des instances.

La configuration de Polr Admin est maintenant terminée, et on peut y accéder à l’adresse http://localhost:8000/polr. Voici la page qui s’affiche.

polr-admin-home

L’onglet Settings affiche une liste déroulante où l’utilisateur peut choisir l’instance de Polr à administrer.

polr-admin-settings

Les autres onglets affichent les données de l’instance courante, à savoir la liste des liens de l’utilisateur, la liste de tous les liens, la liste des utilisateurs et les statistiques d’accès aux liens.

polr-admin-user-links
Les liens de l’utilisateur
polr-admin-admin-links
Tous les liens
polr-admin-users
Les utilisateurs
polr-admin-stats
Statistiques

Le code de cet article se trouve en ligne, à cette adresse: https://github.com/lagdo/polr-admin-sonata.

De Xajax à Jaxon, Armada et Sentry

La version 2.0.0 stable de la librairie Jaxon vient d’être livrée. Elle apporte son lot de nouveautés, parmi lesquelles la gestion des vues et des sessions, une API semblable à jQuery pour mettre à jour le contenu des pages, et les packages Armada et Sentry, qui fournissent des fonctions plus avancées et un socle commun pour les packages d’intégration avec les frameworks PHP.

Cet article présente les évolutions de la librairie depuis sa version 1, avec un focus sur l’architecture de la version 2, et les nouvelles fonctions qui y ont été ajoutées. C’est le premier d’une série d’articles sur Jaxon. Les suivants, plus techniques, présenteront les fonctions les plus importantes de la librairie.

Un peu d’histoire

Jaxon est un fork de la librairie PHP Xajax, qui a apporté une façon unique et originale de créer des applications Ajax avec PHP. Elle permet d’une part de générer des appels Ajax en Javascript vers des classes PHP, et d’autre part de changer le contenu et la présentation de la page dans ces classes PHP (server-side rendering). La programmation d’une application Ajax devient très simple avec Xajax, car un appel Javascript suffit pour exécuter les actions les plus complexes sur la page web, ces actions étant programmées en PHP sur le serveur.
Malheureusement, le développement de la librairie s’est arrêté en 2012, peu après la sortie de la version 0.6, pour des raisons qui restent inconnues.

Une nouvelle version de la librairie a d’abord été publiée sur Github en février 2016, et en mars elle est renommée en Jaxon et publiée pour la première fois sous ce nom. Peu après, en juillet 2016, la version 1.0.0 est sortie. Elle reprenait les principales fonctions de Xajax, mais avec un code entièrement ré-écrit, la séparation de la librairie en un package Javascript et plusieurs packages PHP, l’utilisation de Composer pour l’installation, et quelques plugins. En fin décembre 2016, sortait la première release beta de la version 2. 7 mois, 117 commits et 28 releases plus tard, elle sort enfin en version 2.0.0 stable.

Les nouveautés de Jaxon

Dans la version 1, la principale nouveauté est la possibilité d’enregistrer avec une seule ligne de code les classes PHP se trouvant dans un répertoire, éventuellement en prenant en compte leur namespace. De plus, la configuration de la librairie peut être chargée à partir d’un fichier, et les fonctions pour générer le code Ajax qui appelle les classes PHP s’enrichissent, en particulier avec le support des répertoires, des namespaces, et de la pagination.

Les principales nouveautés de la version 2 sont les packages Armada et Sentry, la gestion des vues et des sessions, et l’API jQuery-PHP. Des tests unitaires ont également été ajoutés à la librairie, bien que la couverture reste à améliorer.

Les packages Armada et Sentry offrent une classe de base dont héritent les classe des applications, et qui leur fournit de nombreuses fonctions. Ils offrent également une API commune pour la gestion des vues et des sessions, qui s’appuie sur les fonctions des frameworks ou d’autres librairies PHP. L’API jQuery-PHP fournit des fonctions inspirées de la librairie Javascript jQuery pour mettre à jour le contenu des pages de l’application en PHP.

L’avantage le plus important de Armada et Sentry est la possibilité de réutiliser les classes de son application Jaxon avec différents frameworks PHP.

Les packages de Jaxon

4 packages constituent le coeur de la librairie Jaxon. Les autres sont des plugins qui lui ajoutent des fonctions, ou l’intègrent à des frameworks.

  • jaxon-core et jaxon-js, qui fournissent respectivement les fonctions PHP et Javascript.
  • jaxon-sentry et jaxon-armada, qui fournissent les fonctions les plus avancées, et un socle commun pour l’intégration avec les frameworks PHP.

Les packages d’intégration simplifient l’utilisation de Jaxon avec les frameworks PHP les plus courants. Ils se présentent sous la forme d’un plugin pour le framework concerné, avec des fonctions pour charger la configuration de Jaxon à partir d’un fichier au format défini par le framework, et pour charger les classes Jaxon à partir d’un répertoire et d’un namespace prédéfinis. Ils fournissent également un adaptateur qui permet d’utiliser l’API de gestion des vues et des sessions de Jaxon avec les fonctions fournies par le framework.

Un seul package d’intégration peut être utilisé dans une application, en fonction du framework utilisé. Pour les frameworks qui ne sont pas encore supportés, le package jaxon-armada doit être utilisé.

Des packages d’intégration existent actuellement pour 6 frameworks PHP :

Les packages Armada et Sentry fournissent une API de vues unique, qui peut être utilisée avec différents moteurs de templates. Dans une application Jaxon, les vues qui utilisent un moteur de templates donné sont placées dans un répertoire, qui est ensuite indiqué dans la configuration, avec l’identifiant du moteur.

    'app' => array(
        'views' => array(
            'users' => array(
                'directory' => '/path/to/users/views',
                'extension' => '.tpl',
                'renderer' => 'smarty',
            ),
        ),
    ),

L’identifiant est ensuite utilisé dans les appels pour permettre d’afficher les templates avec le moteur choisi.

    // Afficher la vue /path/to/users/views/path/to/view.tpl avec Smarty
    $html = $this->view()->render('users::path/to/view');

Le support de chaque moteur de templates est implémenté dans un package distinct, dont plusieurs peuvent être utilisés dans une même application:

Enfin, le package jaxon-dialogs offre une API unique qui peut être utilisée avec différentes librairies Javascript pour afficher des fenêtres, des dialogues de confirmation et des notifications. Au moment de l’écriture de cet article, 16 librairies Javascript sont supportées, et chaque utilisateur peut en ajouter de nouvelles.

Armada et Sentry

Les packages Armada et Sentry sont au coeur de cette nouvelle version 2. Le package Sentry fournit les classes et les interfaces qui implémentent ou définissent toutes les fonctionnalités de la librairie, que ce soit pour la gestion des classes de l’application, ou encore pour la gestion des vues et des sessions. Cependant, il n’est pas prévu pour être utilisé directement par les développeurs. C’est donc le package Armada, qui repose sur Sentry, qui au final va leur offrir toutes ces fontionnalités.

Il est à noter que les packages d’intégration aux frameworks PHP reposent également sur le package Sentry. Par conséquent, cela permet d’avoir des API communes pour les applications Jaxon, qu’elles utilisent Armada ou qu’elles soient intégrées à un framework PHP. En particulier, une classe d’une application qui utilise ces API pourra être réutilisée sans être modifiée avec Armada et avec des frameworks PHP.

Cette propriété est très intéressante car elle ouvre la voie à la création de modules applicatifs complets, avec des fonctions de sessions et de vues, qui peuvent être compatibles avec plusieurs frameworks PHP différents. Cela n’était pas possible jusqu’aujourd’hui.
Un exemple d’un tel module serait une application de gestion des utilisateurs.

Les classes de l’application

Les classes d’une application Jaxon Armada peuvent être réparties dans plusieurs répertoires, chacun ayant un namespace distinct. Tous les répertoires sont déclarés dans la section app.classes du fichier de configuration.

Les classes héritent de la classe Jaxon\Sentry\Armada, qui fournit un grand nombre de fonctions qui vont servir à construire l’application.

  • La mise à jour du contenu et de la présentation des pages de l’application. Ce sont les fonctions qui permettent de manipuler le DOM (Document Object Model) d’une page web, c’est-à-dire de modifier son contenu (HTML) ou sa présentation (CSS).
  • La création des requêtes Ajax vers les classes de l’application. Ces fonctions génèrent le code Javascript nécessaire pour appeler les classes PHP de l’application directement depuis la page web, et aussi pour lier ce code à des évènements sur les éléments de la page.
  • La gestion des vues et des sessions.

Toutes ces fonctions sont définies ou implémentées dans le package Sentry, qui sert de socle commun au package Armada et à tous les autres packages d’intégration des frameworks PHP. Par conséquent, une classe d’une application Armada peut être utilisée avec n’importe quel framework PHP supporté, sans avoir besoin de modification.

Les futurs développements

Ils vont se porter sur trois axes principaux: enrichir l’écosystème de la librairie avec plus de plugins, la rendre plus fiable avec plus de tests, et enfin moderniser sa librairie Javascript.

Sur le premier point, deux nouveaux plugins sont actuellement en projet. Le premier est un plugin pour afficher des graphes dans une page web avec plusieurs librairies Javascript au choix. Il s’inspire du package Charts pour Laravel (https://github.com/ConsoleTVs/Charts). Le second est un module complet de gestion des utilisateurs similaire à Voyager (https://laravelvoyager.com) ou LaraAdmin (http://laraadmin.com), quoiqu’avec beaucoup moins de fonctionnalités. Basé sur Armada et Sentry, il permettra de démontrer la création de modules cross-framework avec Jaxon.

Actuellement, seule une portion du code de la librairie est couverte par des tests unitaires. L’objectif sera d’augmenter la couverture de tests, et aussi d’ajouter des tests fonctionnels, afin qu’il soit possible de valider le bon fonctionnement de la librairie au niveau d’une page web.

Enfin, contraitement au code PHP, le code Javascript de la librairie n’a pas beaucoup évolué. Il serait par exemple intéressant d’utiliser un ou plusieurs micro-frameworks (http://microjs.com) pour implémenter certaines de ses fonctions, avec l’objectif de réduire sa taille mais aussi de lui ajouter des tests.

The new Xajax is out, and the new Xajax is cool!

I started working on a recode of the Xajax library a few weeks ago, and today, I am pleased to present you with the new Xajax library.
The main goal was to use the latest PHP features to make the Xajax library extensible and modular.

So what is it about?

The new Xajax is modular and extensible

The library is now separated into two main packages.

The first package is the core PHP library https://github.com/lagdo/xajax-core.
It provides the main Xajax class, which is now named Xajax, and implements the functionalities needed to export PHP classes to javascript, and handle the Xajax requests and responses.

The second package contains the javascript files of the library https://github.com/lagdo/xajax-js.
Having a javascript only package will later allow to use an open source CDN like https://cdnjs.com/ or https://www.jsdelivr.com/ to deliver the files. By default, the Xajax core library gets the files from a server of mine, which offers HTTP and HTTPS access (thanks to Let’s Encrypt). Of course, they can also be installed on a private server.

This two packages are the minimum requirement to create an Xajax application, however the library can be extended with additional plugins.
I have created three plugins, which are used in the sample codes in the xajax-examples package https://github.com/lagdo/xajax-examples.

https://github.com/lagdo/xajax-toastr:
this package implements javascript notifications with the Toastr JQuery plugin https://github.com/CodeSeven/toastr.

https://github.com/lagdo/xajax-pgwjs:
this package implements responsive modal dialog with the PgwModal library http://pgwjs.com/pgwmodal/.

https://github.com/lagdo/xajax-bootstrap:
this package implements modal dialog with the Twitter Bootstrap library. It requires to manually load the Twitter Bootstrap javascript and CSS files to work properly.

What’s new is this package

First of all, the package is namespaced, uses PSR-4 autoloading, and installs with Composer.
Some features are deprecated, some others, like javascript code generation (see the examples below) are recoded, and some new features are added.

The new feature I consider the most important is the ability to automatically register all the classes in one or more directories, each directory being associated to a namespace or not.
When exporting PHP classes from a directory, the generated javascript classes are named accordingly. In a directory D, the class A in the file D/S/A.php will generate the javascript class S.A, and if the associated namespace is N, the generated javascript class will be N.S.A. As we’ll see in the examples below, the new Xajax library also takes advantage of PHP autoloading to optimize request processing.
This feature makes the Xajax library more suitable for use in modular applications with hundreds of classes.

Examples

Samples code demonstrating the use of the new Xajax library are provided in the xajax-examples package: https://github.com/lagdo/xajax-examples. All examples are based on the helloword.php example in the original Xajax repository https://github.com/Xajax/Xajax/blob/master/examples/helloworld.php. The installation process is described in the homepage of the repository. After the examples are installed, enter the name of any file in the browser to see the result.

– hello.php
This example shows how to export a function with Xajax.

– class.php
This example shows how to export a class with Xajax.

– merge.php
This example shows how to export the generated javascript code in an external file, which is then loaded into the webpage.
You’ll need to create the « deferred » directory, and adapt the parameter of the mergeJavascript() function to your webserver configuration for this example to work.
The compression of the generated javascript code is not yet implemented.

– plugins.php
The example shows the use of Xajax plugins, by adding javascript notifications and modal windows to the class.php example with the xajax-toastr, xajax-pgwjs and xajax-bootstrap packages.
Using an Xajax plugin is very simple. After a plugin is installed with Composer, its automatically registers into the Xajax core library. It can then be accessed both in the Xajax main object, for configuration, and in the Xajax response object, to provide additional functionalities to the application.

– classdirs.php
This example shows how to automatically register all the PHP classes in a set of directories.
The classes in this example are not namespaced, thus they all need to have different names, even if they are in different subdirs.

– namespaces.php
This example shows how to automatically register all the classes in a set of directories with namespaces.
The namespace is prepended to the generated javascript class names, and PHP classes in different subdirs can have the same name.

– autoload-default.php
This example shows how to optimize Xajax requests processing with autoloading.
In all the examples above, an instance of all exported Xajax classes is created when processing a request, while only an instance of the requested class is needed. In an application with a high number of classes, this can cause performance issues.
In this example, the Xajax classes are not registered when processing a request. However, the Xajax library is smart enough to detect that the required class is missing, and load only the necessary file.

– autoload-composer.php
This example illustrates the use of the Composer autoloader.
By default, the Xajax library implements a simple autoloading mechanism by require_once’ing the corresponding PHP file for each missing class. When provided with the Composer autoloader, the Xajax library registers all directories with a namespace into the PSR-4 autoloader, and it registers all the classes in directories with no namespace into the classmap autoloader.

– autoload-disabled.php
In this example the autoloading is disabled in the Xajax library.
The developer then needs to provides its own autoloading, otherwise the Xajax library will raise an error at any attempt to register classes from a directory.
This example will not work until the autoloading is setup.

What’s next?

This version of the package is still an early alpha release, not yet suitable for use in production.
There is still much work to do, and in my point of view the most important ones are:
– Check security, mainly for input data.
– Write tests.
– Write documentation and tutorials.

All ideas, all comments and any help are welcome.
The plugin interface is quite stable now, so anyone willing to write a plugin is encouraged to do so.

Thanks.

Comment lire l’adresse MAC d’un conteneur OpenVZ dans Proxmox

Supposons qu’on soit en train d’automatiser la création et la configuration d’un conteneur dans Proxmox, dans lequel il faut configurer une interface réseau virtuelle (veth). On peut avoir besoin de récupérer l’adresse MAC virtuelle de l’interface réseau du conteneur, par exemple pour y créer un fichier ifcfg-eth0 s’il est sous CentOS.

Le script ci-dessous s’exécute sur un noeud Proxmox et permet de lire l’adresse MAC d’une carte réseau virtuelle dans un conteneur OpenVZ. Pour des raisons de simplicité, les vérifications sur la présence des paramètres sont supprimées.

#!/bin/bash

# Le nom du noeud Proxmox à interroger
NODE=$1
# L'id du conteneur OpenVZ, par exemple 101
VMID=$2
# L'interface réseau, par exemple eth0
IFNAME=$3
pvesh get /nodes/${NODE}/openvz/${VMID}/config 2>/dev/null | grep netif | \
	sed -re "s/.*ifname\=${IFNAME}[^;]*\,mac\=(([0-9A-F]{2}:){5}[0-9A-F]{2}).*/\1/"

Maintenant voyons dans le détail ce que fait ce script.

D’abord, il y a la commande

pvesh get /nodes/${NODE}/openvz/${VMID}/config

qui affiche la configuration d’un conteneur sur un noeud Proxmox.

Le résultat ressemble à ceci pour un conteneur qui a une interface réseau eth0.

{
   "cpus" : 1,
   "cpuunits" : 1000,
   "digest" : "c90",
   "disk" : 50,
   "hostname" : "www.domainname.tld",
   "memory" : 1024,
   "nameserver" : "127.0.0.1",
   "netif" : "ifname=eth0,bridge=vmbr0,mac=C6:BD:74:7D:FF:81,host_ifname=veth105.0,host_mac=D2:83:D5:54:01:69",
   "onboot" : 0,
   "ostemplate" : "centos.tar.gz",
   "quotatime" : 0,
   "quotaugidlimit" : 0,
   "searchdomain" : "domainname.tld",
   "storage" : "local",
   "swap" : 512
}

Ou à ceci pour un conteneur qui a deux interfaces réseau eth0 et eth1.

{
   "cpus" : 1,
   "cpuunits" : 1000,
   "digest" : "c90",
   "disk" : 50,
   "hostname" : "www.domainname.tld",
   "memory" : 1024,
   "nameserver" : "127.0.0.1",
   "netif" : "ifname=eth0,bridge=vmbr0,mac=C6:BD:74:7D:FF:81,host_ifname=veth105.0,host_mac=D2:83:D5:54:01:69;ifname=eth1,bridge=vmbr1,mac=92:C5:0C:F2:BE:25,host_ifname=veth100.1,host_mac=4A:99:39:23:FB:CC",
   "onboot" : 0,
   "ostemplate" : "centos.tar.gz",
   "quotatime" : 0,
   "quotaugidlimit" : 0,
   "searchdomain" : "domainname.tld",
   "storage" : "local",
   "swap" : 512
}

La commande grep netif filtre ce texte pour ne retenir que la ligne qui contient les informations sur les interfaces réseau, et enfin la commande sed -re utilise une expression régulière pour extraire dans cette ligne l’adresse MAC recherchée.

Un mot sur l’expression régulière

s/.*ifname\=${IFNAME}[^;]*\,mac\=(([0-9A-F]{2}:){5}[0-9A-F]{2}).*/\1/

Le s qui la commence indique qu’on souhaite faire une substitution. On parcourt la ligne jusqu’à trouver la chaîne ifname=, suivie de notre interface. A partir de là, on saute tous les caractères jusqu’à trouver la chaîne ,mac=, suivie d’une adresse MAC que matche l’expression régulière ([0-9A-F]{2}:){5}[0-9A-F]{2}.

Il y’a deux remarques à faire à ce niveau. D’abord, il faut noter la présence de la virgule dans la chaîne ,mac=, qui est importante pour ne pas récupérer le paramètre host_mac à la place. Ensuite, les caractères qu’on saute entre le ifname= et le ,mac= doivent absolument être différents du point-virgule, car sinon à cause de la règle du longuest prefix match on récupère systématiquement la dernière adresse MAC présente dans la ligne, qui sera la mauvaise s’il y’en a plusieurs. D’où l’usage de l’expression [^;]*.

Pour que la commande sed renvoie uniquement l’adresse MAC, on utilise en plus de la substitution, une référence arrière. On demande à sed de mémoriser l’adresse MAC en mettant son expression régulière entre parenthèses, puis on fait en sorte que la ligne entière matche l’expression régulière en ajoutant un .* en début et en fin. Le /\1/ substitue la chaîne matchée par la première référence trouvée, et renvoie donc uniquement l’adresse MAC de l’interface réseau de notre conteneur.

Voilà.

Doomedo sur Africa 2.0

J’ai récemment eu l’honneur d’être interviewé par Etum sur son blog Africa 2.0 pour présenter Doomedo, et parler de ses perspectives de développement.
Pour lire l’interview, c’est par ici.

Cette interview marque le début d’une nouvelle phase dans le développement de Doomedo, dont l’objectif est désormais de s’étendre dans d’autres pays d’Afrique francophone après presque deux années sur le marché camerounais. Doomedo est donc à la recherche de partenaires pour accompagner son expansion en Côte d’Ivoire, Sénégal, Gabon, Congo, Bénin, Togo, etc.
Les entrepreneurs intéressés par un partenariat sont invités à prendre contact pour en discuter, à l’adresse partner@doomedo.com ou contact@doomedo.com.

J’en profite pour tirer un coup de chapeau à tous les membres de la Core Team et aux partenaires Doomedo pour tout le travail abattu jusqu’ici, et dire merci à tout ceux qui nous ont soutenus. Merci à toi aussi, Etum.

Coming soon: Maelys on Ubuntu

J’ai le plaisir d’annoncer la sortie prochaine de Maelys on Ubuntu (en abrégé MoU). MoU est un pack qui comprend le logiciel Maelys, son extension Doomedo, le tout dans un Linux Ubuntu préconfiguré avec quelques outils, dont un serveur web et un firewall. Il est disponible sous la forme d’un live CD installable, et d’une machine virtuelle. Dans la grande famille des Ubuntu-based OS, XUbuntu a été choisi pour sa légèreté.

mou1

mou2

L’objectif de MoU est de permettre aux cybercafés qui veulent tester la solution Maelys de le faire sans avoir à réaliser une installation complète, soit en démarrant une machine physique sur le live CD, soit en exécutant la machine virtuelle. Ceux qui veulent adopter la solution pourront par la suite installer définitivement MoU sur leur machine, et dans le cas où ils désirent conserver Windows, ils peuvent continuer à utiliser la machine virtuelle.

MoU est la solution que nous apportons à un problème récurrent, celui de l’adoption de Linux par les utilisateurs, qui ont généralement cette crainte, somme toute légitime, de l’inconnu. MoU doit leur montrer que Linux c’est facile à utiliser, et qu’il est possible de le personnaliser à sa guise.

Pour terminer, j’aimerais donner un grand coup de chapeau à Julius qui a assuré la réalisation technique de cet outil. Merci champion!

Maelys and Doomedo: what next?

Comme je le faisais remarquer dans le post précédent, il me semble que le moment est venu pour Maelys, surtout avec l’arrivée de Doomedo, de faire le bilan et de poser les bases pour passer une nouvelle étape. L’objectif étant de gérer tout le bazar de façon plus professionnelle, et aussi d’élargir les retombées des développements au délà de la simple satisfaction de faire un produit techniquement au point.

Avant d’aller plus loin, je voudrais revenir brièvement sur l’historique de Maelys. Au départ, en 2000-2001, il s’agit d’un défi technique d’un jeune diplômé qui veut comprendre, par la pratique, le cycle complet de développement d’un logiciel, de sa conception à sa distribution. Ensuite, lorsque les premiers utilisateurs commencent à faire des feedbacks, 2005-2006, l’aspect fonctionnel a pris le dessus. L’objectif est alors de faire un logiciel simple à installer et à utiliser, et qui s’adapte à des utilisateurs dont les besoins peuvent varier. Cet objectif a donné lieu à des choix tels que la transformation du module d’administration en application web, ou l’ajout d’un système d’extensions qui permet d’ajouter des fonctionnalités « à la carte ». À partir de fin 2008 enfin, il y a eu la dernière phase où l’objectif est de générer des revenus, même sans être forcément rentable (ce qui sera probablement l’objectif de la prochaine phase), avec notamment le service Doomedo.

Suite à ça donc, je me dis que Maelys, après avoir atteint une certaine maturité technique, doit maintenant s’attaquer à une nouvelle étape. Pour ça, il y a deux aspects sur lesquels je sais qu’il va falloir que je prenne des décisions, et je ne sais pas encore très bien lesquelles.

Il y a d’abord l’aspect communautaire. Maelys a bien un forum où ses utilisateurs interviennent de temps à autre, mais on ne peut pas dire que ceux-ci constituent réellement une communauté. C’est un peu chacun qui utilise Maelys dans son coin, et vient poser son problème quand il en rencontre un. En gros, il y a un manque de suivi et coordination. La grande question ici est de savoir comment faire pour créer une vraie communauté dans laquelle les utilisateurs se sentent réellement impliqués, et qui soit reconnue comme telle. Un corollaire doit être ajouté  à cette question, qui concerne en particulier les utilisateurs en Afrique, puisque c’est eux la première cible du logiciel.

Ensuite, il y a les aspects marketing et rentabilité qui je dois l’avouer n’ont jamais vraiment été sérieusement pris en compte. Pour poser la question avec des termes connus, je dirais simplement: quel modèle économique pour Maelys? Il y’en a bien sûr un qui se dégage avec Doomedo, mais pourrait-on en trouver d’autres? Quels avantages, risques et inconvénients? Comment faire de Maelys une marque?

A moins un…

Comme aurait dit le Prési, à moins un!

Nessa que l’autre jour, je me promène comme d’habitude sur le web (moteurs de recherche, forums, etc.) pour entendre un peu ce qui se dit de Maelys. Et là, je tombe sur cette annonce. Ainsi donc, il y a eu quelqu’un quelque part qui était prêt à débourser 500 euros pour se faire un installateur Linux pour Maelys. Il a dû bénir la philosophie du Libre, le mec, le jour où il est revenu sur le site de Maelys et c’est rendu compte que les fameux installateurs y étaient disponibles… gratuitement. 500 euros économisés, en ces temps de crise, c’est pas rien!