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.