Migration des plugins 0.8 => 0.9

Le système de plugins de Galette 0.9 est incompatible avec les versions précédentes. Cela ne signifie pas que vous deviez récrire l’ensemble de votre plugin, mais vous devez procéder à des adaptations.

Généralités

Bien sûr, vous devez vous référer au guide de développement des plugins pour tous les détails.

Certains objets de Galette ont été modifiés pour supprimer des variables globales. Il est désormais nécessaire de leur envoyer ces informations, comme :

<?php
//old call
$obj = new \Galette\Core\Object();
//new call - $this->zdb is accessible from routes
$obj = new \Galette\Core\Object($this->zdb);

Nos avons basé nos exemples sur le plugin ObjectsLend,référez-vous au commit en question (`326b52f4) <https://git.tuxfamily.org/galette/plugin-objectslend.git/commit/?h=develop&id=326b52f486c6dccd5896d9db13e3a074d3896b19>`_.

_define.php

Deux nouveaux paramètres ont été ajoutés :

  • un nom, utilisé pour le routage et les domaines de traduction, une simple chaîne à ajouter entre la version de compatibilité de galette et le date du plugin,
  • configuration de la liste des limitations d’accès aux URL (lorsque le middleware $authenticate est utilisé), dans un tableau à la fin de la configuration existante.

Un fichier define.php pour Galette 0.8 :

<?php
$this->register(
    'Galette Objects Lend',             //Name
    'Manage rent/lend of object',       //Short description
    'Mélissa Djebel, Johan Cwiklinski', //Author
    '0.5-alpha',                        //Version
    '0.8.0',                            //Galette version compatibility
    '2017-01-21',                       //Date
    null //Permissions needed - not yet implemented
);

Après avoir été mis à jour pour Galette 0.9, le fichier ressemblera à :

<?php
$this->register(
    'Galette Objects Lend',             //Name
    'Manage rent/lend of object',       //Short description
    'Mélissa Djebel, Johan Cwiklinski', //Author
    '0.5-alpha',                        //Version
    '0.8.0',                            //Galette version compatibility
    'objectslend',                      //routing name and translation domain
    '2017-01-21',                       //Date
    [   //Permissions needed - not yet implemented
        'routename'      => 'acl',
        'anotherroute'   => 'anotheracl'
    ]
);

Les ACL des routes sont gérés durant le développement du plugin.

_config.php

Il était conseillé de créer un variable pour préfixer les gabarits d’affichage, ce n’est plus utile.

_routes.php

Ce fichier devient le centre de votre plugin. Tous les anciens fichiers PHP qui étaient directement appelés seront maintenant dispatchés en plusieurs routes (une par fichier PHP, ou pas).

Par exemple, prenons la page preferences.php de notre plugin. Le code source pour la 0.8 était :

<?php
use Galette\Entity\ContributionsTypes;
use GaletteObjectsLend\Preferences;

define('GALETTE_BASE_PATH', '../../');
require_once GALETTE_BASE_PATH . 'includes/galette.inc.php';
if (!$login->isLogged() || !$login->isAdmin()) {
    header('location: ' . GALETTE_BASE_PATH . 'index.php');
    die();
}

require_once '_config.inc.php';

$lendsprefs = new Preferences($zdb);

if (isset($_POST['saveprefs'])) {
    unset($_POST['saveprefs']);
    if ($lendsprefs->store($_POST, $error_detected)) {
        $success_detected[] = _T("Preferences have been successfully stored!");
    }
}

$tpl->assign('page_title', _T("ObjectsLend preferences"));

$ctypes = new ContributionsTypes();
$tpl->assign('ctypes', $ctypes->getList());
$tpl->assign('lendsprefs', $lendsprefs->getpreferences());

//Set the path to the current plugin's templates,
//but backup main Galette's template path before
$orig_template_path = $tpl->template_dir;
$tpl->template_dir = 'templates/' . $preferences->pref_theme;

//display success and error messages
$tpl->assign('error_detected', $error_detected);
$tpl->assign('success_detected', $success_detected);

$content = $tpl->fetch('preferences.tpl', LEND_SMARTY_PREFIX);
$tpl->assign('content', $content);
//Set path to main Galette's template
$tpl->template_dir = $orig_template_path;
$tpl->display('page.tpl', LEND_SMARTY_PREFIX);

Cette page fournissait à la fois l’affichage et l’enregistrement des préférences du plugin. Nous remplacerons ce comportement par deux routes distinctes : l’une avec la méthode HTTP GET qui sera en charge de l’affichage et ue autre avec la méthode HTTP POST pour gérer le stockage lui même. Dans notre _routes.php nous aurons :

<?php
use Galette\Entity\ContributionsTypes;
use GaletteObjectsLend\Preferences;

//Constants and classes from plugin
require_once $module['root'] . '/_config.inc.php';

$this->get(
    __('/preferences', 'routes'),
    function ($request, $response, $args) use ($module, $module_id) {
        if ($this->session->objectslend_preferences !== null) {
            $lendsprefs = $this->session->objectslend_preferences;
            $this->session->objectslend_preferences = null;
        } else {
            $lendsprefs = new Preferences($this->zdb);
        }

        $ctypes = new ContributionsTypes($this->zdb);

        $params = [
            'page_title'    => _T('ObjectsLend preferences', 'objectslend'),
            'ctypes'        => $ctypes->getList(),
            'lendsprefs'    => $lendsprefs->getpreferences()
        ];

        // display page
        $this->view->render(
            $response,
            'file:[' . $module['route'] . ']preferences.tpl',
            $params
        );
        return $response;
    }
)->setName('objectslend_preferences')->add($authenticate);

$this->post(
    __('/preferences', 'routes'),
    function ($request, $response, $args) use ($module, $module_id) {
        $post = $request->getParsedBody();
        $lendsprefs = new Preferences($thiszdb);

        $error_detected = [];
        if ($lendsprefs->store($pos, $error_detected)) {
            $this->flash->addMessage(
                'success_detected',
                _T("Preferences have been successfully stored!", "objectslend")
            );
        } else {
            //store object in session to keep entered values in case of error
            $this->session->objectslend_preferences = $lendsprefs;
            foreach ($error_detected as $error) {
                $this->flash->addMessage(
                    'error_detected',
                    $error
                );
            }
        }

        return $response
            ->withStatus(301)
            ->withHeader(
                'Location',
                $this->router->pathFor('objectslend_preferences')
            );
    }
)->setName('store_objectlend_preferences')->add($authenticate);

Vous pourrez constater que les URL des deux routes sot les mêmes, mais avec une méthode HTTP (et bien sûr les noms des routes !) différents. Pour les besoins de notre plugin, l’accès sera restreint aux administrateurs. Nous avons déjà mis en place l’appel au middleware dans notre exemple précédent, nous devons maintenant ajouter les ACL pour ces nouvelles routes dans _define.php :

<?php
//[...]
    [   //Permissions needed - not yet implemented
        'objectslend_preferences'        => 'admin',
        'store_objectlend_preferences'   => 'admin'
    ]

Tous les traitements de preferences.php ont été déplacés, nous pouvons supprimer le fichier.

templates/default/menu.tpl

Puisque l’URL de la page préférences de notre plugin a changé, nous devons adapter menu.tpl :

<li{if $PAGENAME eq "preferences.php"} class="selected"{/if}>
    <a href="{$galette_base_path}{$lend_dir}preferences.php">{_T string="Preferences"}</a>
</li>
<li{if $cur_route eq "objectslend_preferences"} class="selected"{/if}>
    <a href="{path_for name="objectslend_preferences"}">{_T string="Preferences" domain="objectslend"}</a>
</li>

Il y a trois modifications ici :

  • L’appel au fichier PHP a été remplacé par un appel à la route,
  • la condition de sélection doit être changée, cela ne peut plus être basé sur le nom de fichier,
  • le domaine de traduction a été utilisé pour les chaînes à traduire, ce n’est pas toujours une nécessité si vous utilisez les chaînes du coeur telles quelles (ce qui aurait du être le cas ici).

Ressources web

Toutes les ressources qui doivent être accessibles depuis le serveur web doivent être traitées de manière particulière. Vous devez les déplacer dans le dossier webroot de votre plugin :

$ mkdir webroot
$ git move includes/featherlight-1.7.0 webroot
$ git move templates/default/galette_lend.css webroot
$ git move templates/default/lend.js webroot
$ git move templates/default/images webroot

Ensuite, vous devez changer les chemins vers ces fichiers. Par exemple, voyez le fichier header.tpl qui ressemble en 0.8 :

<link rel="stylesheet" type="text/css" href="{$galette_base_path}{$lend_tpl_dir}galette_lend.css" media="screen"/>
{if isset($olendsprefs) && $olendsprefs->showFullsize()}
<link rel="stylesheet" type="text/css" href="{$galette_base_path}{$lendc_dir}featherlight-1.7.0/featherlight.min.css" media="screen"/>
<script type="text/javascript" src="{$galette_base_path}{$lendc_dir}featherlight-1.7.0/featherlight.min.js"></script>
{/if}
<script type="text/javascript" src="{$galette_base_path}{$lend_tpl_dir}lend.js"></script>

Et qui deviendra en 0.9 :

<link rel="stylesheet" type="text/css" href="{path_for name="plugin_res" data=["plugin" => $module_id, "path" => "galette_lend.css"]}" media="screen"/>
{if isset($olendsprefs) && $olendsprefs->showFullsize()}
<link rel="stylesheet" type="text/css" href="{path_for name="plugin_res" data=["plugin" => $module_id, "path" => "featherlight-1.7.O/featherlight.min.css"]}" media="screen"/>
<script type="text/javascript" src="{path_for name="plugin_res" data=["plugin" => $module_id, "path" => "featherlight-1.7.O/featherlight.min.js"]}"></script>
{/if}
<script type="text/javascript" src="{path_for name="plugin_res" data=["plugin" => $module_id, "path" => "lend.js"]}"></script>

Note

Les noms de dossiers des ressources n’a pas d’importance, leur chemin est conditionné par l’identifiant unique du plugin ($module_id).

Fichiers de gabarits

Les fichiers de gabarit Smarty doivent déclarer leur héritage, ce qui était auparavant fait depuis des appels PHP.

Pour tous les fichiers gabarits de votre plugin, vous devez à minima ajouter {block name="content"} et {/block} autour de votre contenu et ajouter l’instruction d’héritage. Les appels javascript doivent être déplacés dans le bloc optionnel javascript.

Pour suivre notre exemple, le fichier preferences.tpl de votre plugin doit changer comme suit :

{extends file="page.tpl"}
{block name="content"}
    <form action="{path_for name="store_objectlend_preferences"}" method="post">
       <!-- contenu -->
    </form>
{/block}

Note

L’action du formulaire a également été changée pour suivre les routes définies.

Langues

Les locales dans Galette utilisent désormais des domaines de traduction. Makefile et xgettext.py doivent être mis à jour, copez les depusi un plugin officiel à jour.

Makefile doit être adapté pour déclarer les langues et domaines utilisés :

LANGUAGES = en_US fr_FR.utf8
DOMAINS = objectslend

Les fichiers existants doivent être renommés pour suivre les langues et domaines, les fichiers PHP ne sont plus utilisés, vous pouvez les supprimer :

$ git mv messages.po objectslend.pot
$ git mv en_US.po objectslend_en_US.po
$ git mv fr_FR.utf8.po objectslend_fr_FR.utf8.po
$ git mv en_US/LC_MESSAGES/galette_objectslend.mo en_US/LC_MESSAGES/objectslend.mo
$ git mv fr_FR.utf8/LC_MESSAGES/galette_objectslend.mo fr_FR.utf8/LC_MESSAGES/objectslend.mo
$ git rm lang_french.php
$ git rm lang_english.php

Et finalement, vous devrez ajouter le domaine lorsque nécessaire dans votre code. Il est certainement possible d’utiliser un script pour ce faire, mais ce n’était pas nécessaire pour ce plugin.