Ez-publish ( 75 articles - Voir la liste )

Astuce [eZ5] Authentifier un utilisateur programmatiquement

Pour authentifier programmatiquement un utilisateur en front-office, vous pouvez utiliser cette méthode :

<?php

use eZ\Publish\Core\MVC\Symfony\Security\User as SecurityUser;
use eZ\Publish\API\Repository\Values\User\User;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

    /**
     * Connecte l'utilisateur en argument.
     *
     * @param User $user
     *   Utilisateur eZ Publish récupéré depuis le repository
     *
     * @throws \Exception Si une erreur survient lors de la récupération du service de gestion de jeton.
     */
    public function login(User $user)
    {
        // Authentification pour Symfony
        $roles =['ROLE_USER'];
        $security_user = new SecurityUser($user, $roles);
        $security_user->setAPIUser($user);

        $token = new UsernamePasswordToken($security_user, null, 'ezpublish_front', $roles);
        $this->container->get('security.token_storage')->setToken($token);

        // Authentification pour le repo eZ Publish
        $this->repository->setCurrentUser($user);
    }

Marque-page [eZ5] Les répertoires dans le cœur d'eZ Publish 5

Le cœur d'eZ Publish 5 se trouve dans le répertoire vendor/ezsystems/ezpublish-kernel/ de l'application.

Voici quelques répertoires utiles qu'il contient :

  • eZ/Publish/API/Repository/ : contient les interfaces des services avec les signatures de toutes leurs méthodes.
  • eZ/Publish/Core/ : contient l'implémentation des interfaces du répertoire précédent
    • Repository/ : contient l'implémentation de ces mêmes services.
    • Base/Exceptions/ : contient toutes les exceptions fournies par eZ, et surtout leurs constructeurs.
    • Persistence/Legacy/Content/Search/Gateway/CriterionHandler/ : contient les critères de recherche fournis par eZ.
    • Persistence/Legacy/Content/Search/Gateway/SortClauseHandler/ : contient les méthodes de tri fournies par eZ.

Remarque :

Les 4 derniers répertoires, sont sous eZ/Publish/Core/.

Astuce [eZ5] Rechercher des contenus par mot clé

L'extension eZTags pour eZ Publish fournit un système de mots clés pour regrouper des contenus par thématique. Avec elle arrive un nouveau type de champ, pour taguer vos contenus.

Dans eZ Publish 4.x (ou en mode legacy), le template de ce champ affiche un lien vers une page qui liste les contenus avec ce mot clé. La version pour eZ Publish 5 est disponible ici. Malheureusement, elle ne fournit aucune méthode pour trouver des contenus à partir d'un mot clé.

Voici trois méthodes pour récupérer ces contenus.

<?php

// [...]

use \Netgen\TagsBundle\API\Repository\Values\Tags\Tag;

/**
 * Retrouve les contenus avec le mot clé en argument.
 *
 * @param string $keyword Mot clé recherché
 * @param int $offset Offset pour les résultats de la recherche
 * @param int $limit Nombre maximal de résultats de recherche
 *
 * @return \eZ\Publish\API\Repository\Values\Content\Content[]
 *
 * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException Si l'utilisateur courant n'a pas le droit de voir les tags
 * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException Si aucun tag avec cet ID n'existe
 */
public function getRelatedContentByKeyword($keyword, $offset = 0, $limit = 50) {

    $rootTagID = 2;

    // Recherche du tag correspond au mot clé
    $tag = $this->getTagByKeyword($rootTagID, $keyword);

    $relatedContentList = array();

    if (!empty($tag)) {

        // Recherche des contenus avec le mot clé souhaité
        $tagService         = $this->container->get('ezpublish.api.service.tags');
        $relatedContentList = $tagService->getRelatedContent($tag, $offset, $limit);
    }

    return $relatedContentList;
}

/**
 * Retrouve un Tag à partir de son mot clé.
 * Le premier trouvé parmi les descendants de celui dont l'ID est en argument est retourné.
 *
 * @param string $rootTagID ID du tag parmi les descendants duquel rechercher
 * @param string $keyword Mot clé recherché
 *
 * @return \Netgen\TagsBundle\API\Repository\Values\Tags\Tag
 *
 * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException Si l'utilisateur courant n'a pas le droit de voir le tag courant
 * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException Si aucun tag avec cet ID n'existe
 */
public function getTagByKeyword($rootTagID, $keyword) {

    $tag = null;

    // Récupération du tag racine
    $tagService = $this->container->get('ezpublish.api.service.tags');
    $rootTag    = $tagService->loadTag($rootTagID);

    if (!empty($rootTag)) {

        // Récupération des tags descendants
        $descendantTagList = $this->getTagDescendantList($rootTag);

        if (!empty($descendantTagList)) {

            // Parcours des tags descendants
            for ($i = 0, $length = count($descendantTagList); $i < $length && $tag == null; $i++) {

                if ($descendantTagList[$i]->keyword == $keyword) { 
                    $tag = $descendantTagList[$i];
                }
            }
        }
    }

    return $tag;
}

/**
 * Retourne tous les tags descendant de celui en argument.
 *
 * @param \Netgen\TagsBundle\API\Repository\Values\Tags\Tag $rootTag Tag racine
 *
 * @return \Netgen\TagsBundle\API\Repository\Values\Tags\Tag[]
 *
 * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException Si l'utilisateur courant n'a pas le droit de voir le tag courant
 */
public function getTagDescendantList( Tag $rootTag ) {

    // Récupération des tag descendants
    $descendantTagList = array();

    $tagService       = $this->container->get( 'ezpublish.api.service.tags' );
    $childrenTagList  = $tagService->loadTagChildren( $rootTag );

    // Parcours des tags enfants
    foreach ( $childrenTagList as $childTag ) {

        $descendantTagList[] = $childTag;

        // Récupération des descendants
        $descendantTagList = array_merge( $descendantTagList, $this->getTagDescendantList( $childTag ) );
    }

    return $descendantTagList;
}

Remarques :

  • Ces méthodes peuvent être utilisées dans un contrôleur, où l'attribut $container (ContainerInterface) est disponible.
  • Tous vos tags doivent avoir une racine commune (= une seule arborescence). Cette racine servira de base pour les recherches.
  • Dans la première méthode, la variables $rootTagID (identifiant du tag racine) est en dur et devrait être récupérée depuis un fichier de configuration.
  • Dans cet exemple, la recherche ne fonctionne pas pour les synonymes. La deuxième méthode peut être améliorée pour les gérer.

Astuce [eZ5] Ajouter des filtres et des fonctions à Twig

Twig fournit de nombreuses fonctions et une liste de filtres pour simplifier le développement des templates.

Quelques exemples :

{# Des fonctions natives : #}
Contenu d'une variable : {{ dump(my_var) }}
Nombre aléatoire : {{ random(5) }}

{# Des filtres natifs : #}
Taille d'un tableau : {{ my_array|length }}
Mise en minuscule : {{ my_string|upper }}
Échappement de caractère : {{my_string|escape}}

L'intérêt de Twig c'est qu'il est très facilement extensible, et vous vous pouvez créer vos propres fonctions et vos propres filtres. Par exemple :

{# Une nouvelle fonction : #}
Affiche l'Url actuelle : {{ current_uri() }}

{# Un nouveau filtre : #}
{{ "Ma phrase est trop longue parce que la fin n'est pas intéressante."|truncate(28) }}

Prérequis

  • Vous avez déjà créé le Bundle Acme/MyBundle, et l'avez activé dans le fichier ezpublish/EzPublishKernel.php.

Remarque :

Si ce n'est le nom du fichier de kernel, tout cet exemple est valable pour une application Symfony 2 non eZ.

Création de l'extension Twig

L'ajout de filtres et fonctions se fait via un fichier PHP, qu'on appelle une extension Twig.

Créez le répertoire Twig/ dans votre bundle, et le fichier MyExtension.php à l'intérieur :

<?php
namespace AT\APIToolsBundle\Twig;

use \Symfony\Component\DependencyInjection\ContainerInterface;

class MyExtension extends \Twig_Extension {

    /**
     * @var \Symfony\Component\DependencyInjection\ContainerInterface;
     */
    protected $container;

    /**
     * Contructeur de l'extension Twig MyExtension.
     *
     * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
     */
    public function __construct(ContainerInterface $container) {

        $this->container = $container;
    }

    /**
     * Retourne le nom de l'extension.
     *
     * @return string
     */
    public function getName() {

        return 'MyExtension';
    }

    /**
     * Retourne la liste des Filtres de template à ajouter à Twig.
     *
     * @return array
     */
    public function getFilters() {

        return [
            'truncate' => new \Twig_Filter_Method($this, 'truncate'),
        ];
    }

    /**
     * Retourne la liste des Fonctions de template à ajouter à Twig.
     *
     * @return array
     */
    public function getFunctions() {

        return [
            'current_uri' => new \Twig_Function_Method($this, 'getCurrentURI'),
        ];
    }

    /**
     * Retourne l'URI courante.
     *
     * @return string $_SERVER['REQUEST_URI']
     */
    public function getCurrentURI() {

        return $_SERVER['REQUEST_URI'];
    }

    /**
     * Tronque le texte en argument.
     * Si la longueur du texte est supérieure à $maxLength, $suffix est ajouté à la chaîne.
     *
     * @param string $text Chaîne à tronquer
     * @param int $maxLength Longueur maximale autorisée pour la chaîne
     * @param string $suffix Le sufixe à ajouter si besoin
     * @return string
     */
    public function truncate($text, $maxLength, $suffix = '...') {

        $truncatedText = $text;

        mb_internal_encoding('UTF-8');

        $length      = mb_strlen($text );
        $sufixlength = mb_strlen($suffix);

        // Si le texte est trop long
        if ($length > $maxLength && $length >= $sufixlength) {

            $truncatedText = mb_substr($text, 0, $maxLength - $sufixlength) . $suffix;
        }

        return $truncatedText;
    }
}

Explications :

  • La classe MyExtension étend la classe Twig_Extension fournie par Symfony.
  • La méthode getName() retourne le nom de votre choix pour votre extension.
  • Les méthodes getFilters() et getFunctions() retournent la liste des filtres et des fonctions à ajouter à Twig.
  • Le nom du filtre ou de la méthode est défini par la clé dans le tableau (ici truncate et current_uri).
  • Pour instancier un nouveau filtre, on utilise new \Twig_Filter_Method($this, 'méthode_à_appeler').
  • Et de la même manière, pour une nouvelle fonction new \Twig_Function_Method($this, 'méthode_à_appeler').
  • Les deux dernières méthodes sont celles appelées dans les constructeurs. Elles contiennent le code métier qui effectue le traitement.

Informer Symfony

L'extension Twig est terminée mais Symfony ne sait pas encore qu'elle existe. Il vous faut la déclarer en tant que service, dans le fichier Resources/config/services.yml de votre bundle :

parameters:
    acme_my.twig_extension.class: Acme\MyBundle\Twig\MyExtension

services:
    acme_my.twig_extension:
        class: %acme_my.twig_extension.class%
        arguments: [@service_container]
        tags:
            - { name: twig.extension }

Explications :

  • Chaque paramètre et chaque service du fichier a un identifiant unique (ex : acme_my.twig_extension).
  • On définit la classe MyExtension comme paramètre. Si on déplace ou renomme la classe par la suite, seul le paramètre sera à changer.
  • On déclare l'extension Twig en tant que service, en spécifiant la classe à utiliser et l'argument à passer au constructeur.
  • On tague le service avec twig.extension pour que Symfony sache de quel type de service il s'agit.

Astuce [eZ5] Créer une page de login

Objectif

Le but de cet article est de proposer un exemple de page de connexion. Il s'articule autour de deux fichiers principaux : un template Twig et un contrôleur PHP.

Il est réalisé entièrement en mode Symfony, sans utiliser le stack Legacy.

Si l'utilisateur saisit de mauvais identifiants, un message d'erreur est affiché. Une fois connecté, il est redirigé vers la page qu'il consultait avant de se connecter.

Prérequis

  • Vous avez déjà créé le Bundle Acme/MyBundle, et l'avez activé dans le fichier ezpublish/EzPublishKernel.php.
  • Le pagelayout utilisé par défaut est AcmeMyBundle::pagelayout.html.twig. Il possède un bloc nommé col_main.
  • Dans le pagelayout, la variable redirect_uri doit être définie et contenir l'url courante (ex: /Ma-rubrique/Mon-article).

Création du template

Dans le répertoire de templates de votre bundle (Resources/views/), créez les répertoires user/connection/, qui contiendront tous les templates pour la connexion des utilisateurs (connexion, inscription, ...)

Créez ensuite le fichier login.html.twig dans le répertoire user/connection/.

Voici à quoi il peut ressembler :

{# Surcharge du bloc 'col_main' du template pagelayout.html.twig, pour la page de connexion #}
{% extends noLayout ? viewbaseLayout : "AcmeMyBundle::pagelayout.html.twig" %}

{#
    Affiche un article en mode Full

    Paramètres :
    - noLayout      : False
    - fail_login    : Si la connexion a échouée
    - redirect_uri  : URI vers laquelle rediriger après la connexion
 #}

{% block col_main %}

<div class="main-content user user-login panel">

    {# Titre #}
    <header class="heading">
        <h1>Connexion</h1>
    </header>

    {# Contenu #}
    <div class="content-body">

        {# Message d'erreur #}
        {% if fail_login %}
            <div class="alert alert-danger">
                <button data-dismiss="alert" class="close" type="button">×</button>
                <p><strong>Erreur !</strong></p>
                <p>Identifiant ou mot de passe invalide.</p>
            </div>
        {% endif %}

        {# Formulaire #}
        <form class="form-horizontal" method="post"
              action="{{ path( 'acme_my_user_login', { 'redirectURI': redirect_uri } ) }}">

            <div class="form-group {% if fail_login %}has-error{% endif %}">
                <label class="col-lg-3 control-label" for="login">Identifiant</label>
                <div class="col-lg-4">
                    <input type="text" placeholder="Identifiant" 
                           id="login" name="Login" class="form-control input-small">
                </div>
                <div class="validation col-lg-5">
                    <img src="{{ asset( "bundles/acmemy/images/error.png" ) }}" alt="Erreur" />
                </div>
            </div>

            <div class="form-group {% if fail_login %}has-error{% endif %}">
                <label class="col-lg-3 control-label" for="password">Mot de passe</label>
                <div class="col-lg-4">
                    <input type="password" placeholder="Mot de passe" 
                           id="password" name="Password" class="form-control input-small">
                </div>
                <div class="validation col-lg-5">
                    <img src="{{ asset( "bundles/acmemy/images/error.png" ) }}" alt="Erreur" />
                </div>
            </div>

            <div class="form-group">
                <div class="col-lg-10 text-right">
                    <button class="btn btn-primary" type="submit">Connexion</button>
                </div>
            </div>
        </form>
    </div>
</div>
{% endblock %}

Explications :

  • Ligne 2, on commence par étendre le pagelayout par défaut pour modifier le bloc principal.
  • Juste en dessous, en commentaire, on liste les paramètres disponibles/nécessaires dans le template.
  • Ensuite, on commence la surcharge du bloc principal de la page : col_main.
  • On affiche un message d'erreur si les identifiants sont mauvais ({% if fail_login %}).
  • On affiche un formulaire avec un champ identifiant et un champ mot de passe.
  • Le formulaire pointe vers la route acme_my_user_login, sur laquelle Symfony va brancher le futur contrôleur. L'URL courante lui sera transmise en argument.

Remarques :

  • L'image d'erreur doit être présente dans le répertoire Resources/public/images/ du bundle.
  • Ce template utilise le framework CSS Bootstrap 3.
  • Les textes devraient être entre les tags {% trans %} et {% endtrans %}, pour pouvoir être traduits facilement par la suite si besoin.

Création du contrôleur

C'est le contrôleur qui va gérer les actions de connexion et déconnexion des utilisateurs. Il aura donc deux actions : login et logout.

Créez le fichier UserConnectionController.php dans le répertoire Controller/ de votre Bundle :

<?php
namespace Acme\MyBundle\Controller;

use \eZ\Bundle\EzPublishCoreBundle\Controller;
use \eZ\Publish\API\Repository\Exceptions\NotFoundException;
use \eZ\Publish\API\Repository\Values\User\User;
use \Symfony\Component\HttpFoundation\Cookie;
use \Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * Controleur pour gérer les vues de connexion des utilisateurs.
 */
class UserConnectionController extends Controller {

    /**
     * Gestion de l'affichage de la page de login.
     *  - Affiche la page de login par défaut
     *  - Affiche d'éventuelles erreur de connexion
     *  - Connecte et redirige l'utilisateur
     *
     * @param string URI vers laquelle rediriger après la déconnexion
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function loginAction( $redirectURI ) {

        $failLogin = false;

        // Suppression d'un éventuel doublon dans l'URI
        if ( ( $offset = strpos( $redirectURI, '/user/login' ) ) === 0 ) {
            $redirectURI = substr( $redirectURI, $offset );
        }

        $request = $this->getRequest()->request;

        // Si le formulaire de connexion a été soumis
        if ( $request->has( 'Login' ) && $request->has( 'Password' ) ) {

            $login       = $request->get( 'Login' );
            $password    = $request->get( 'Password' );

            if ( trim( $login ) != '' && trim( $password ) != '' ) {
                $userService = $this->getRepository()->getUserService();

                try {
                    $user = $userService->loadUserByCredentials( $login, $password );
                    return $this->connectUser( $user, $redirectURI );

                } catch (NotFoundException $e) {
                    $failLogin = true;
                }
            } else {
                $failLogin = true;
            }
        }

        return $this->render(
            'AcmeMyBundle:user\connection:login.html.twig',
            array(
                'noLayout'      => false,
                'fail_login'    => $failLogin,
                'redirect_uri'  => $redirectURI
            )
        );
    }

    /**
     * Déconnecte l'utilisateur courant.
     *
     * @param string URI vers laquelle rediriger après la déconnexion
     *
     * @return \Symfony\Component\HttpFoundation\Response
     *
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException Si l'utilisateur anonyme n'existe pas
     */
    public function logoutAction( $redirectURI ) {

        // Suppression d'un éventuel doublon dans l'URI
        if ( ( $offset = strpos( $redirectURI, '/user/logout' ) ) === 0 ) {
            $redirectURI = substr( $redirectURI, $offset );
        }

        // Récupération de l'utilisateur anonyme
        $userService  = $this->getRepository()->getUserService();
        $anonymousUser = $userService->loadAnonymousUser();

        return $this->connectUser( $anonymousUser, $redirectURI );
    }

    /**
     * Connecte l'utilisateur en argument et retourne une redirection 302.
     * Si l'utilisateur en argument est anonyme, alors c'est une déconnexion.
     *
     * @param \eZ\Publish\API\Repository\Values\User\User $user L'utilisateur à connecter
     * @param string $redirectURI URI vers laquelle rediriger l'utilisateur après connexion
     *
     * @return RedirectResponse
     */
    protected function connectUser( User $user, $redirectURI = '/' ) {

        $repository = $this->getRepository();
        $repository->setCurrentUser( $user );
        $session    = $this->getRequest()->getSession();

        try {
             $response = new RedirectResponse( $redirectURI );

        } catch (NotFoundException $e) {
             $response = new RedirectResponse( '/' );
        }

        $userService    = $repository->getUserService();
        $anonymousUser  = $userService->loadAnonymousUser();

        // Si l'utilisateur en argument est anonyme
        if ( $user->id === $anonymousUser->id ) {
            // Déconnexion de l'utilisateur courant
            $response->headers->setCookie( new Cookie( 'is_logged_in', 'false' ) );
            $session->remove( 'eZUserLoggedInID' );

        } else {
            // Connexion de l'utilisateur
            $response->headers->setCookie( new Cookie( 'is_logged_in', 'true' ) );
            $session->set( 'eZUserLoggedInID', $user->id );
        }

        return $response;
    }
}

Explications :

  • Le contrôleur est une classe PHP qui étend la classe Controller du Bundle eZ\Bundle\EzPublishCoreBundle fourni par eZ Publish.
  • Ses deux premières méthodes sont des actions et leurs noms finissent d'ailleurs par Action. Elles retournent un objet Response.
  • La troisième est une méthode utilitaire pour connecter un utilisateur.
  • Par défaut, loginAction() inclue le template de login dans sa réponse, grâce aux lignes :
return $this->render(
    'AcmeMyBundle:modules/user:login.html.twig',
    array(
        'noLayout'      => false,
        'fail_login'    => $failLogin,
        'redirect_uri'  => $redirectURI
    )
);
  • Si le formulaire est soumis et valide, on appelle la méthode connectUser() qui connecte l'utilisateur et on fournit une réponse de type redirection.

Configuration de la route

Pour que Symfony sache quoi faire lorsqu'on appelle la page http://mon-site/user/login, il faut modifier le fichier Resources/config/routing.yml dans votre bundle.

Ajoutez-y les deux routes suivantes, pour se connecter et se déconnecter :

acme_my_user_login:
    pattern:  /user/login{redirectURI}
    defaults: { _controller: AcmeMyBundle:UserConnection:login }
    requirements:
        redirectURI: ".*"

acme_my_user_logout:
    pattern:  /user/logout{redirectURI}
    defaults: { _controller: AcmeMyBundle:UserConnection:logout }
    requirements:
        redirectURI: ".*"

Explications :

  • Une route doit être nommée par un identifiant unique de votre choix (ex : acme_my_user_login). C'est lui qui est appelé dans la fonction path() côté template.
  • Elle associe un pattern d'URL, à l'action d'un contrôleur.
  • Pour la première route, si on appelle la page /user/login<quelque chose>, la méthode loginAction() du contrôleur UserConnectionController du bundle AcmeMyBundle sera exécutée. La méthode recevra quelque chose en argument.
  • Les éléments dans requirements permettent entre autres de spécifier la forme que doit avoir le paramètre. Ici, n'importe qu'elle chaîne est autorisée.

Astuce [eZ5] Créer un bundle

Qu'est-ce qu'un Bundle ?

Bundle est le nom Symfony pour un module, une brique, une extension (terme eZ Publish 4.X). Il peut contenir tout ou une partie du design, une API technique, une fonctionnalité particulière, ...

Il peut être dépendant d’autres Bundles, mais est réutilisable. Il est identifié par un nom de domaine (namespace) et un nom de Bundle (finissant par Bundle), le tout concaténé.

Création du Bundle

La création du Bundle peut se faire via ligne de commande, à partir du répertoire racine de l’application eZ Publish :

php ezpublish/console generate:bundle --namespace="MonNamespace/MonBundle"

Explications :

  • Le paramètre namespace est le nom du namespace et le nom du Bundle concaténés.
  • Le nom de domaine peut être le nom du projet, celui de l’entreprise, ... Il peut contenir des /.
  • Le nom du Bundle doit finir par Bundle.

Nom du Bundle

L’assistant de création du Bundle propose alors le nom final du Bundle : MonNamespaceMonBundle.

Appuyez sur Entrée pour conserver ce nom standard.

Remarque :

Si vous créez le Bundle principal de votre application, et que vous souhaitez avoir un namespace et un nom de Bundle identique, c'est à cette étape que vous pouvez simplifier le nom final pour éviter d'avoir MonNamespaceMonNamespaceBundle.

Emplacement du Bundle

Vous pouvez ensuite choisir le chemin où se trouvera le Bundle.

Par défaut il sera créé dans le répertoire src/ à la racine de l’application eZ Publish, ce qu'il est préférable de conserver : appuyez sur Entrée.

Format de la configuration

Choisissez yml comme format de configuration, et validez avec Entrée.

Génération et autres configurations

Pour une première fois, choisissez de générer la structure complète du Bundle.

Appuyez sur Entrée pour confirmer toute la génération.

Même chose pour les questions suivantes.

Fichiers générés

L’assistant de création de Bundle a généré l’arborescence suivante :

Arborescence bundle généré

Comme tout bundle Symfony, il se compose de 3 répertoires principaux :

  • Controller/ : vous y créerez vos contrôleurs (équivalents de vos modules dans eZ4).
  • Resources/ : s'y trouvent tous les fichiers non PHP (templates Twig, fichiers de config, js, CSS, ...).
  • Tests/ : répertoire contenant vos tests unitaires.

Erreur [eZ5] FatalErrorException: Error: Class 'XSLTProcessor' not found

Si vous rencontrez l'erreur suivante après l'installation d'eZ Publish 5 :

FatalErrorException: Error: Class 'XSLTProcessor' not found in 
[...]\vendor\ezsystems\ezpublish-kernel\eZ\Publish\Core\FieldType\XmlText\Converter\Html5.php line 77

C'est que l'extension xsl n'est pas activée pour PHP.

Erreur [eZ5] The extension "ext/fileinfo" must be loaded in order for this class to work

Lorsque vous migrez vers eZ Publish Community Project 2013.06, vous pouvez rencontrer cette erreur :

The extension "ext/fileinfo" must be loaded in order for this class to work.

Fileinfo est une extension pour PHP. Elle est généralement déjà packagée sous Linux, mais pas sous Windows avec WampServer.

Activez l'extension php_fileinfo et redémarrez apache.

Marque-page [eZ] Créer un datatype

Dans eZ Publish 4, un datatype est un type de champ pour une classe de contenu.

EZ Publish en fournit un certain nombre : image, ligne de texte, texte riche, case à cocher, nombre, ... Vous pouvez également créer votre propre datatype, pour facilité la contribution et l'affichage d'un champ.

On peut par exemple imaginer un champ couleur, avec côté back-office une pipette ou une palette de couleur pour choisir facilement sa couleur.

Voici un tutoriel complet pour créer son propre datatype, rédigé par Jérôme Vieilledent et Nicolas Pastorino pour PHP Solutions.

Astuce [eZ5] Développez en mode dev

Avec eZ Publish 5, pour activer la console de développement de Symfony vous devez modifier la configuration Apache, à priori dans votre virtual host.

Remplacez index.php par index_dev.php :

DirectoryIndex index.php
...
RewriteRule .* /index.php

Remarque :

EZ Publish et Symfony doivent également être configurés en mode développement.

Erreur [eZ5] L'arbre des contenus n'est plus disponible dans le back-office

Avec eZ Publish 5, le mot de passe de la base de données est stocké à deux endroits : dans eZ et dans Symfony.

Si tout fonctionne correctement dans le back-office, excepté l'arbre des contenus qui n'apparait pas, c'est probablement que les deux mots de passes sont différents.

Pour vérifier qu'ils sont bien configurés, vérifier dans ces deux fichiers :

  • ezpublish_legacy/override/site.ini.append.php
  • ezpublish/config/ezpublish.yml

S'il y a d'autres problèmes dans le back-office, comme l'impossibilité de naviguer dans les contenus via les éléments enfants, c'est sans doute un problème de cache.

Astuce [eZ4] Les préférences utilisateur

Le module user d'eZ Publish fournit une vue pour stocker simplement des préférences utilisateurs : user/preferences.

Concrètement, ces préférences sont stockées en base de données, avec pour clé le couple (user_id, preference_name) et pour valeur celle de notre choix.

Ce fonctionnement est souvent utilisé en back-office, pour afficher tel ou tel bloc de la page, comme la barre de droite par exemple.
Pour la partie publique côté front-office il est à éviter, car tous les utilisateurs anonymes auront la même préférence. (Ou alors il ne faut pas leur permettre de modifier la valeur.)

Créer/modifier une préférence

Côté template ou HTML

Il suffit d'appeler l'URL user/preferences, en proposant par exemple un lien à l'utilisateur.

Pour afficher/masquer la barre de droite du back-office, par exemple on a juste ce genre de liens :

{* Afficher la barre *}
<a href="{'user/preferences/set/admin_right_menu_show/1'|ezurl('no')}">
    Afficher la barre de droite
</a>

{* Masquer la barre *}
<a href="{'user/preferences/set/admin_right_menu_show/0'|ezurl('no')}">
    Masquer la barre de droite
</a>

Explication :

Pour créer/modifier une préférence, il faut appeler la vue user/preferences, avec 3 paramètres : set, le nom de la préférence puis sa valeur.

Côté PHP

Il faut utiliser la méthode setValue() de la classe eZPreferences :

eZPreferences::setValue( 'my_preference_name', 'my_value' );

Remarque :

Par défaut, la préférence sera associée à l'utilisateur connecté. Un troisième argument est disponible ($user_id), pour l'affecter à un autre utilisateur.

Récupérer la valeur de la préférence

Côté template

{ezpreference( 'my_preference_name' )}

Explication :

L'utilisateur courant est automatiquement utilisé.

Côté PHP

Il faut utiliser la méthode value() de la classe eZPreferences :

eZPreferences::value( 'my_preference_name' );

Remarques :

  • Pour récupérer la valeur pour un autre utilisateur que celui connecté, utilisez le second argument facultatif.
  • La méthode values() de la classe eZPreferences permet de récupérer toutes les préférences d'un utilisateur.

Astuce [eZ4] Différences entre les méthodes variable() et variableArray() de la classe eZINI

La classe eZINI propose deux méthodes pour récupérer des variables sous forme de tableau : variable() et variableArray().

variable()

C'est la méthode habituelle que vous utilisez pour récupérer des chaînes de caractères. Par exemple pour récupérer la valeur dans cette configuration :

[MySection]
MyProperty=value

La méthode retournera :

value1

Elle fonctionne aussi pour un tableau de valeurs :

[MySection]
MyProperty[]
MyProperty[]=value1
MyProperty[]=value2
...

La méthode retournera :

Array (
  '0' => value1
  '1' => value2
  ...
)

variableArray()

Cette méthode permet de récupérer un tableau de valeurs pour ce genre de configuration :

[MySection]
MyProperty=value1;value2;value3;...

La méthode retournera :

Array (
  '0' => value1
  '1' => value2
  '2' => value3
  ...
)

Astuce [eZ5] Générer la configuration Symfony du projet existant

Lorsque vous migrez un site eZ Publish 4.x vers 5.x sans utiliser l'assistant d'installation automatisée, vous devez générer la configuration Symfony de votre site.

EZ Publish 5 est maintenant un projet Symfony et utilise la gère sa configuration en fichiers .yml. Pour passer des anciens .ini vers les nouveaux .yml, utilisez la commande suivante à la racine de votre projet :

php ezpublish/console ezpublish:configure --env=prod <group> <admin-siteaccess>

Remplacez <group> par votre groupe de siteaccess (ex: mon_site), et <admin-siteaccess> par le nom du siteaccess de votre back-office.

Remarques :

  • Symfony permet de switcher simplement entre les environnements de production et de développement. Pour chacun d'eux elle propose un fichier de configuration par défaut : ezpublish/config/ezpublish_dev.yml et ezpublish/config/ezpublish_prod.yml.
    Remplacez --env=prod par --env=dev pour utiliser la configuration de développement.
  • Le groupe de siteaccess est une nouvelle notion introduite par eZ Publish 5.

Astuce [eZ5] Les liens symboliques dans eZ Publish 5

EZ Publish 5 utilise le principe des assets de Symfony. Les fichiers statiques (css, js, images, ...) que vous utilisez dans vos bundles doivent donc être aussi présents dans le répertoire web/.

De plus, tous les fichiers uploadés via le back-office (qui est encore en eZ Publish 4), sont stockés par défaut dans le répertoire ezpublish_legacy/var/storage/. De la même manière, ils doivent aussi se retouver dans le répertoire web/ pour être servis par apache.

Pour mettre à jour votre répertoire web/, vous avez le choix entre copier tous les fichiers statiques, ou créer des liens symboliques.

Pour cela eZ Publish a surchargé la console de Symfony, et vous propose ces deux commandes (à lancer à la racine de votre projet) :

php ezpublish/console assets:install --symlink web
php ezpublish/console ezpublish:legacy:assets_install --symlink web

Explications :

  • La première commande crée des liens symboliques dans le répertoire web/, pointant vers les ressources des bundles.
  • La seconde crée des liens pointant vers les ressources du répertoire ezpublish_legacy/.

Remarque :

L'option --symlink web est facultative. Si vous la retirer (ou si elle ne fonctionne pas), eZ Publish créera des copies des fichiers au lieu des liens symboliques.

Astuce [eZ4] Utiliser l'API Ajax d'eZ Publish

L'extension ezjscore d'eZ Publish permet d'appeler des fonctions PHP via des requêtes Ajax. Vous pouvez l'utiliser pour mettre à jour une partie de la page sans la recharger complètement.

Le principe

Le javascript va lancer une requête Ajax à la vue call du module ezjscore (et donc appeler l'URL /ezjscore/call). Cette vue va retourner le résultat d'une méthode PHP, en fonction de la configuration du fichier ezjscore.ini.

Le résultat est alors disponible côté js et peut être utilisé pour modifier une partie de la page.

PHP

Les méthodes disponibles pour un appel Ajax doivent être implémentées dans des classes héritant de ezjscServerFunctions.

Par exemple dans le fichier monextension/classes/MyServerCallFunctions.php :

<?php

/**
 * Classe de fonctions à appeler en ajax.
 */
class MyServerCallFunctions extends ezjscServerFunctions {

    /**
     * Retourne le message "Hello x !", avec x le premier élément du tableau de paramètres, 
     *  ou "world" si aucun paramètre n'est passé.
     *
     * @param array $args Arguments
     * @return string
     */
    public static function helloMessage( array $args ) {

        // Log de l'appel de la fonction
        eZLog::write( 'Appel Ajax : ' . __METHOD__, 'debug.log' );

        if ( !empty( $args ) ) {
            $message = 'Hello ' . $args[0] . ' !';
        } else {
            $message = 'Hello world !';
        }
        return $message;
    }
}

Configuration

Pour que votre classe soit utilisable vous devez la déclarer le fichier ezjscore.ini :

Par exemple, dans le fichier monextension/settings/ezjscore.ini.append.php :

<?php /* #?ini charset="utf-8"?

[ezjscServer]
# Liste des fonctions accessibles via des appels Ajax
FunctionList[]=my_function_name

[ezjscServer_my_function_name]
# Nom de la classe PHP
Class=MyServerCallFunctions
# Nom du fichier contenant la classe
File=extension/monextension/classes/MyServerCallFunctions.php
# Nom des fonctions proposées par la classe
Functions[]=my_function_name

*/ ?>

Remarque :

Une fois le fichier modifié, videz les caches et régénérez les autoloads, pour qu'eZ Publish trouve votre nouvelle classe.

Un premier test

Vous pouvez déjà appeler votre fonction en tapant l'url suivante dans votre navigateur :

http://monsite.com/index.php/ezjscore/call/my_function_name::helloMessage::dude

On appelle bien la vue call du module ezjscore, à laquelle on fournit le nom d'une fonction et la liste des arguments, séparés par ::.

Remarque :

Cela ne fonctionne que si vous êtes connecté en tant qu'administrateur. Pour éviter ça, vous devez autorisez d'autres rôles à accéder à la vue ezjscore/call. Vous pouvez même définir des limitations, pour n'autoriser que l'accès à la fonction my_function_name.

Javascript

Maintenant que votre fonction est accessible, voici comment l'utiliser dans vos template.

Tout d'abord, vous devez ajouter le code suivant à votre template, pour inclure l'API Javascript d'ezjscore :

{ezscript_require( array( 'ezjsc::jquery', 'ezjsc::jqueryio' ) )}

Remarques :

  • L'exemple présenté utilise jQuery. Vous pouvez également utiliser l'API YUI fournie avec eZ.
  • Le premier élément du tableau (ezjsc::jquery) est facultatif si vous avez déjà inclus jQuery dans votre page, et peut même poser problème si la version de jQuery incluse est différente.

Voici maintenant le code Javascript :

var  dataSent = {arg0: 'dude', arg1: 'not_used'};
$.ez( 
    'my_function_name::helloMessage::dude::not_used',
    dataSent,
    function(data) {        
        // Si l'appel Ajax a retourné une erreur
        if ( data.error_text ) {
            console.error('Erreur : ' + data.error_text )
        // Si l'appel Ajax a retourné des résultats
        } else if ( data.content.length > 0 ) {
            console.info('Résultat : ' + data.content);
        }
    }
 );

Explications :

  • Si une erreur se produit, le message est disponible dans la variable data.error_text.
  • Si l'appel réussit, le résultat est présent dans la variable data.content.

Astuce [eZ4] Utiliser les alias de fetch

Pour simplifier l'utilisation des fetch dans les templates, eZ Publish propose d'utiliser des alias.

Exemple d'utilisation

Par exemple, si vous voulez compter récursivement les articles fils du nœud courant, le fetch standard serait :

{def $nb = fetch( 'content', 'tree_count',
    hash( 
        'parent_node_id', $node.node_id,
        'class_filter_type', 'include',
        'class_filter_array', array( 'article' ) 
    ) 
)}

Si vous utilisez souvent ce même fetch, vous aimerez sans doute lui créer un alias. La syntaxe devient alors :

{def $nb = fetch_alias( 
    'children_article_count', 
    hash( 'parent_node_id', $node.node_id ) 
)}

Explications :

  • L'alias s'appelle children_article_count()
  • Il n'a qu'un seul paramètre : l'ID du nœud parent.

Configuration

Pour informer eZ Publish de votre alias, il faut le déclarer dans le fichier fetchalias.ini.

Par exemple, dans le fichier monextension/settings/fetchalias.ini.append.php :

<?php /* #?ini charset="utf-8"?

[children_article_count]
# Compte récursivement le nombre d'articles sous le nœud dont l'ID est en paramètre
Module=content
FunctionName=tree_count
Constant[class_filter_type]=include
Constant[class_filter_array]=article
Parameter[parent_node_id]=parent_node_id

*/ ?>

Explications :

  • La section (children_article_count) est le nom de votre alias, à utiliser dans votre template.
  • Le module et la fonction sont ceux que vous auriez appelés dans le fetch standard.
  • Les constantes sont les paramètres fixes que vous auriez passés au fetch standard.
  • Les paramètres permettent de mapper le nom des paramètres de l'alias avec ceux du fetch standard.

Remarques :

  • N'oubliez pas de vider les caches pour qu'eZ Publish prennent en compte cette configuration.
  • EZ Publish fournit déjà des alias, visibles dans le fichier settings/fetchalias.ini.

Astuce [eZ4] Créer ses fetch personnalisés

EZ Publish fournit un grand nombre de fonctionnalités, accessibles dans les templates via des fetch (voir la documentation).

Vous pouvez créer vos propres fetch via le système de function_definition des modules.

Le module

  • Commencez par créer un nouveau module (ou utilisez un module déjà existant dans votre extension).
  • Si c'est un nouveau, vous devez le déclarer dans le fichier module.ini.

Par exemple, dans le fichier monextension/settings/module.ini.append.php :

<?php /* #?ini charset="utf-8"?

[ModuleSettings]
ExtensionRepositories[]=monextension
ModuleList[]=monmodule

Le PHP

Vous n'avez qu'un seul fichier à créer dans votre extension : modules/monmodule/function_definition.php.

Ce fichier contient un tableau php qui liste les fonctions disponibles. Pour chacune d'elle, vous préciserez son nom, son type (lecture ou écriture), la méthode PHP à appeler et les paramètres à lui passer.

Par exemple :

<?php
$FunctionList = array();

$FunctionList['tree_unique'] = array( 
    'name' => 'tree_unique',
    'operation_types' => array( 'read' ),
    'call_method' => array( 
        'class' => 'ATContentFunctionCollection',
        'method' => 'fetchObjectTree' 
    ),
    'parameter_type' => 'standard',
    'parameters' => array( 
        array( 
            'name' => 'parent_node_id',
            'type' => 'integer',
            'required' => true 
        )  
    ) 
);

Explications :

  • Cette fonction s'appelle tree_unique
  • Elle est de type lecture (elle n'effectue pas de modifications sur les données, mais en retourne)
  • Elle retourne le résultat de la méthode fetchObjectTree() de la classe ATContentFunctionCollection
  • Elle a un paramètre obligatoire : l'ID du nœud parent

Remarque :

Vous devez bien sur avoir créer une classe ATContentFunctionCollection possédant la méthode fetchObjectTree (). Le nom de la classe n'a pas d'importance, mais dans le cœur d'eZ Publish on ajoute FunctionCollection pour mettre en évidence les fonctionnalités utilisables dans les fetch.

Dans les templates

Vous pouvez maintenant utiliser votre fetch après avoir vider les caches. Utilisez-le comme n'importe quel fetch natif :

{def $node_list = fetch( 
    'monextension', 'tree_unique', 
    hash( 'parent_node_id', $node.node_id ) 
)}

Marque-page [eZ4] Récupérer les urls et les chemins vers les répertoires

EZ Publish fournit la classe eZSys, propose pas mal de méthodes pour récupérer par exemple :

  • Le chemin vers le répertoire var
  • L'url du serveur
  • Le port utilisé
  • Le chemin vers le répertoire du projet
  • La version de php
  • ...

Cette classe se trouve dans lib/ezutils/classes/ezsys.php.

Marque-page [eZ4] Un système de chat

Si vous avez besoin d'un système de chat dans votre site eZ Publish, voici une extension qui peut faire l'affaire : eZ phpFreeChat.

Interface de l'extension eZ phpFreeChat

Elle fournit un module pour eZ Publish, dont l'unique vue affiche un chat. Le chat est composé d'une discussion principale visible par tous les utilisateurs connectés au chat et permet des discussions privées entre deux utilisateurs (en cliquant sur leur nom).

Remarque :

L'extension utilise la version 1.3 de phpfreechat, qui existe maintenant en 2.1.1.

Astuce [eZ4] Un système de forums élaboré

EZ Publish fournit par défaut 3 classes de contenus pour créer des forums : Forum, Forum topic et Forum reply. L'extension ezwebin fournit les templates associés et vous pouvez ainsi créer des forums, des conversations et des messages.

Malgré cela, il manque beaucoup de fonctionnalités courantes attendues sur un forum : une messagerie privée, un suivi des discussions, des statistiques, du BBCode, un affichage en tableau des forums et des sujets, une gestion de rangs, de la modération, la possibilité de signaler un abus, ...

Ces fonctionnalités sont implémentées par l'extension xrowForum. Elle fournit :

  • une interface dans le back-office pour administrer les modérateurs, les rangs et les paramètres des forums.
  • une nouvelle classe de contenu Forums, qui permet de regrouper dans un même affichage tous les forums enfants.
  • une interface front-office de messagerie privée et d'ajout de contacts

L'extension distribuée sur le repo svn est mal internationalisée et mal traduite. Voici la même version corrigée.

Astuce [eZ4] Configuration Apache et PHP pour eZ Publish avec WampServer

Voici la configuration à utiliser pour développer un site eZ Publish avec WampServer. Elle s'articule autour de 3 fichiers de configuration :

  • httpd.conf : Fichier de configuration du serveur Apache
  • httpd-vhosts.conf : Fichier de configuration apache pour les hôtes virtuels
  • php.ini : Configuration de php

Remarque :

Dans cet exemple, la version 2.2 64 bits de WampServer est utilisée, avec Apache 2.2.22 et PHP 5.3.13.

Configuration finale

  • Url du site : http://mon_site.loc/index.php
  • Chemin absolu vers le projet : D:\Dev\php_projects\MonSite\
  • Chemin absolu vers WampServer : D:\Dev\wamp\

Httpd.conf

Ce fichier se trouve dans bin\apache\apache2.2.22\conf\, à partir de la racine de WampServer.

Tout en bas du fichier, activez les hôtes virtuels en décommentant cette ligne :

Include conf/extra/httpd-vhosts.conf

Voici un exemple du fichier httpd.conf.

Modules Apache

Via l'interface de Wamp, activez les modules Apache suivants :

Modules Apache activés

Hôtes virtuels

Éditez le fichier host (C:\Windows\System32\drivers\etc\hosts) en tant qu'administrateur.

Pour pouvoir l'enregistrer avec Notepad++, lancez l'éditeur en tant qu'administrateur (Clic-droit sur l'exécutable) et ouvrez ensuite le fichier host.

Ajoutez la ligne

127.0.0.1          mon_site.loc

Lorsque vous utiliserez l'adresse mon_site.loc, votre navigateur saura ainsi qu'il s'agit de votre ordinateur et non pas d'une machine sur internet.

Modifiez maintenant le fichier httpd-vhosts.conf (ex : D:\Dev\wamp\bin\apache\apache2.2.22\conf\extra\httpd-vhosts.conf) en ajoutant les lignes :

<VirtualHost *:80>
    ServerName mon_site.loc
    DocumentRoot D:/Dev/php_projects/MonSite

    <Directory D:/Dev/php_projects/MonSite>
        Options Indexes FollowSymLinks MultiViews
        Order allow,deny
        allow from all
    </Directory>
</VirtualHost>

Voici un exemple du fichier httpd-vhosts.conf.

Php.ini

WampServer utilise deux fichiers php.ini différents. Le premier est utilisé par votre eZ Publish ou n'importe quel site servi par votre serveur Apache. Le second est utilisé lorsque vous appelez PHP via l'invite de commande. Voici où les trouver depuis la racine de votre dossier WampServer :

  • bin\apache\apache2.2.22\bin\php.ini
  • bin\php\php5.3.13\php.ini

Le plus simple est d'utiliser la même configuration entre les deux lorsque vous êtes en phase de développement.

Voici les propriétés à éditer :

# Temps maximum d'exécution de script (en s)
max_execution_time = 480 
# Temps maximum pour uploader un fichier
max_input_time = 180
# Taille maximale des fichiers uploadables
post_max_size = 8M

# Zone de date
date.timezone = "Europe/Paris"

Voici un exemple du fichier php.ini.

Remarque :

Vous pouvez également activer XDebug pour pouvoir déboguer de manière optimale.

Extensions PHP

Comme pour les modules Apache, activez ces extensions PHP via l'interface de WampServer :

Extensions de PHP activées

Erreur [eZ4] Les fichiers sont importés à la racine et pas dans la médiathèque

Vous pouvez modifier l'emplacement où les contenus sont créés par défaut, selon leur classe de contenu. Ainsi, eZ Publish définit par exemple qu'un contenu Image doit être placé dans medias/images et qu'un contenu Fichier dans medias/files.

Si vous renommez ces emplacements (ex: Fichiers à la place de Files) et lancez le cronjob de régénération des urls, medias/files devient medias/fichiers.

Si vous ne modifiez pas la configuration, eZ Publish ne trouve plus l'emplacement et importe les fichiers à la racine.

Vous devez donc surcharger le fichier content.ini en réécrivant les emplacements déjà existants et surtout en modifiant ceux que vous avez renommés.

Par exemple, dans le fichier monextension/settings/content.ini.append.php :


<?php /* #?ini charset="utf-8"?

[RelationAssignmentSettings]
ClassSpecificAssignment[]
ClassSpecificAssignment[]=user,user_group;utilisateurs/membres
ClassSpecificAssignment[]=image;medias/images
ClassSpecificAssignment[]=video;medias/multimedia
ClassSpecificAssignment[]=file;medias/fichiers
ClassSpecificAssignment[]=quicktime;medias/multimedia
ClassSpecificAssignment[]=windows_media;medias/multimedia
ClassSpecificAssignment[]=real_video;medias/multimedia
ClassSpecificAssignment[]=flash;medias/multimedia
*/ ?>

Astuce [eZ4] Priorité de surcharge des fichiers .ini

Dans eZ Publish on utilise tout le temps les fichiers de configuration .ini. Il y en a un peu partout dans l'arborescence d'un projet eZ Publish et il est donc facile de s'y perdre.

Où trouver les fichiers .ini ?

  • Toujours dans un répertoire settings.
  • Dans le répertoire settings/ à la racine du projet ou dans ceux des extensions.

Différence en les fichiers .ini et .ini.append.php

Les fichiers .ini sont les fichiers par défaut, fournis par eZ Publish et par les extensions. Lorsque vous surchargez ces fichiers, utilisez les fichiers .ini.append.php (ex: site.ini.append.php).

Si dans votre extension, vous avez besoin de créer un nouveau fichier de configuration, utilisez donc l'extension .ini. Si vous distribuer votre extension et que la personne qui l'utilise veur surcharger une propriété, elle créera elle, un fichier .ini.append.php.

La hiérarchie de surcharge

Voici l'ordre de prise en compte des fichiers .ini (et .ini.append.php), du moins important au plus important. Les chemins sont relatifs à la racine de l'application.

  • settings/ : Vous y trouverez tous les fichiers de configuration par défaut. Ils impactent indifféremment tous les siteaccess. Ces fichiers ne doivent pas être modifiés !
  • extension/mon_extension/settings/siteaccess/mon_siteaccess/ : Seule la configuration du siteaccess mon_siteaccess sera impactée.
  • settings/siteaccess/mon_siteaccess/ : Idem, seule la configuration du siteaccess mon_siteaccess sera impactée.
  • extension/mon_extension/settings/ : Le cas le plus couramment utilisé. Vous modifier ici toutes les configurations que vous voulez, pour tous les siteaccess.
  • settings/override/ : C'est le niveau le plus haut. Tout ce qui est ici sera pris en compte en priorité, quel que que soit le siteaccess.

Priorité entre les extensions

L'ordre d'activation des extensions dans le fichier site.ini a une importance. La première extension déclarée surcharge la deuxième, qui surcharge la troisième et ainsi de suite.

Astuce [eZ4] Afficher le contenu d'une variable

Pour connaitre le contenu d'une variable dans eZ Publish il existe plusieurs pour faciliter le débogage.

Côté PHP

La fonction print_r()

Cette fonction couplée avec les balises <pre>, affiche le contenu de la variable de manière récursive et structurée.

echo '<pre>';
print_r( $variable, false);
echo '</pre>';

affichera

print_r()

La fonction var_dump()

Cette fonction affiche la même chose que print_r(), mais de manière typée. Contrairement à l'autre, elle affiche aussi les valeurs null ou false.

echo '<pre>';
var_dump( $variable );
echo '</pre>';

affichera

print_r()

La fonction eZDebug::writeDebug()

Cette fonction affiche la même chose que la fonction var_dump(), mais dans la partie debug de bas de page (si le mode debug est activé).

eZDebug::writeDebug( $variable );

affichera

print_r()

Dans un template

L'opérateur attribute()

Cet opérateur affiche le contenu de tous les attributs de la variable, si celle-ci est un objet ou un tableau. Le second paramètre définit le niveau de profondeur à afficher.

{$variable|attribute( 'show', 1 )}

affichera

print_r()

{$variable|attribute( 'show', 2 )}

affichera

print_r()

La fonction debug-log

Cette fonction fait la même chose que eZDebug::writeDebug(), mais côté template.

{debug-log var=$variable}

affichera

print_r()

Astuce [eZ4] Créer un lien de téléchargement d'un fichier

Pour créer un lien de téléchargement vers le fichier d'un contenu de type File, utilisez :

{concat( 
    'content/download/', $object.data_map.file.contentobject_id,
    '/', $object.data_map.file.id, 
    '/version/', $object.data_map.file.version , 
    '/file/', $object.data_map.file.content.original_filename|urlencode() 
)|ezurl( 'no' )}

Explication :

La variable $object doit être un objet de contenu, soit une instance de eZContentObject.

Astuce [eZ4] Consommer des services web fournis par Play!

Voici une extension eZ Publish permettant de récupérer des webservices fournis par Play! : AT Play Connector.

Cette extension propose une application Play! d'exemple, correspondant aux exemples des articles précédents. Pour chaque service web de cette application, une méthode PHP et un fetch() permettent de récupérer son résultat côté eZ Publish.

Ces méthodes sont implémentées dans le fichier classes/ATWSFunctionCollection.php (à partir de la racine de l'extension), dont voici un extrait :

<?php

class ATWSFunctionCollection extends ATWebServices {

    /**
     * Appel du service web /test
     *
     * @param string $nom Nom des locataires recherchés
     * @param string $dateNaissance Date de naissance  des locataires recherchés
     * @param string $lang Langue des résultats
     */
    public static function test() {

        // Nom du service web
        $wsName = '/test';

        // Construction de la requête
        $wsRequest = $wsName;

        return array( 'result' => parent::getJSONResult( $wsRequest ) );
    }

    /**
     * Appel du service web /helloWorld
     *
     * @param string $name Nom de la personne à saluer
     */
    public static function helloWorld( $name ) {

        // Nom du service web
        $wsName = '/helloWorld';

        // Construction de la requête avec les paramètres
        $wsRequest = $wsName . '?';

        if ( $name != null ) {

            $wsRequest .= 'name=' . urlencode( $name );
        }

        return array( 'result' => parent::getJSONResult( $wsRequest ) );
    }
}

Explications :

  • Cette classe étend la classe ATWebservices également fournie par l'extension.
  • Chaque méthode ne fait que préparer le début d'une requête HTTP, avec le nom du service web et les paramètres à envoyer.
  • Ces requêtes HTTP sont complétées et soumises au services web de Play! via la méthode getJSONResult() de la classe ATWebServices.

Le fichier modules/atplayws/function_definition.php (à partir de la racine de l'extension) déclare des fetch() utilisables depuis les templates :

<?php

$FunctionList = array( );

$FunctionList['test'] = array(
        'name' => 'test',
        'operation_types' => array( 'read' ),
        'call_method' => array(
                'class' => 'ATWSFunctionCollection',
                'method' => 'test' ),
        'parameter_type' => 'standard',
        'parameters' => array( )
);

$FunctionList['hello_world'] = array(
        'name' => 'hello_world',
        'operation_types' => array( 'read' ),
        'call_method' => array(
                'class' => 'ATWSFunctionCollection',
                'method' => 'helloWorld' ),
        'parameter_type' => 'standard',
        'parameters' => array(
                array( 'name' => 'name',
                        'type' => 'string',
                        'required' => true ),
        )
);

Explications :

Pour chaque fetch, on déclare son nom, la méthode PHP correspondante à appeler, ainsi que les paramètres à lui fournir.

Remarque :

Pour plus de détails sur l'utilisation de l'extension, consultez le fichier README.txt présent à sa racine.

Astuce Créer des services web avec Play!

Tracer sa route

Play! vous permet de créer des services web accessibles via de simples requêtes HTTP.

Vous devez pour cela définir des routes, pour que l'application sache quelle méthode exécuter en fonction de la requête HTTP.

C'est le rôle du fichier conf/route, dont voici un exemple :

# Routes
# Ce fichier définit les différentes routes (avec les routes prioritaires en premier)
# ~~~~

# Mappe les fichiers de ressources statiques du répertoire /public avec le chemin /assets dans l'URL
GET     /assets/*file          controllers.Assets.at(path="/public", file)

GET     /test                  controllers.Application.test()
GET     /helloYou              controllers.Application.helloYou(firstName, lastName)
GET     /userList              controllers.Application.userList()

Explications :

  • Pour chaque service web, on définit une route de la forme : <Type de requête (GET|POST)> <url> <package.Classe.method()>.

  • L'appel de l'URL http://localhost:9000/test retournera le résultat de la méthode test() de la classe Application du package controllers.

  • La méthode helloYou() nécessite les arguments firstName et lastName.
    Pour l'appeler, l'url sera http://localhost:9000/helloYou?firstName=Jean-Louis&lastName=David.
    Comme la requête est définie en GET, il suffit d'ajouter les paramètres dans l'url sous la forme nom_param=valeur.

Remarque :

La première route (GET /assets/*file...) est un peu particulière et sert au fonctionnement interne de Play!, pour gérer les fichiers statiques de l'application.

Une méthode qui a la classe (et vice-versa)

Voici un aperçu de la classe Java qui contient les méthodes définie dans le fichier route :

package controllers;

import java.util.Date;

import models.WSResult;
import models.beans.Bean;
import models.beans.MessageBean;
import play.libs.Json;
import play.mvc.Controller;
import play.mvc.Result;

public class Application extends Controller {

    public static Result test() {

        WSResult result = new WSResult();

        result.addBean(new Bean() {
            public String message = "Test"; 
            public Date date = new Date(); 
        });

        return ok(Json.toJson(result));
    }

    public static Result helloYou(String firstName, String lastName) {

        WSResult result;

        if (!firstName.equals("") && !lastName.equals("")) {
            result = new WSResult();
            result.addBean(new MessageBean("Hello " + firstName + " " + lastName + "!"));
        } else {
            result = new WSResult("Paramètres incorrectes.", WSResult.ERROR_CODE_CUSTOM_PARAMS);
        }

        return ok(Json.toJson(result));
    }
}

Explications :

  • La classe Application étend la classe Controller fournie par Play!.
  • Pour chaque URL définie dans le fichier route, on retrouve bien la méthode dans la classe Application, avec ses arguments. (Notez que les noms des arguments doivent être identiques.)
  • Chaque méthode est statique et retourne un objet Result (classe également fournie par Play!).
  • Dans cet exemple, à chaque Result est associée une liste d'objets Bean.
  • Un objet Bean regroupe des informations à rendre accessible via un service web (un message, le résultat d'un requête SQL, ...)
  • Dans cet exemple, la classe Result a été étendue par la classe WSResult, pour lui ajouter une liste d'objets Bean, un éventuel message d'erreur, et d'autres informations.
  • Chaque méthode retourne un objet Result en JSON, via la méthode ok(Json.toJson(result)) fournie par Play!.
  • La seconde méthode vérifie si les paramètres sont vides. Si oui, alors l'objet Result ne contiendra pas de Bean mais un message et un code d'erreur. Si non, il contiendra un Bean comme dans la première méthode.

Un vrai Bean, monsieur

Normalement, un Bean représente une ligne de données provenant d'une source (base de données, fichier, ...). C'est une classe Java des plus simples, avec juste des attributs et leurs getter() et setter().

Imaginons par exemple qu'on veuille représenter un utilisateur de site web présent dans une table de base de données, on pourrait avoir :

package models.beans;

public class UserBean extends Bean {

    private int userID;
    private String email;
    private String login;
    private String password;

    public static final String FIELD_USER_ID  = "user_id";
    public static final String FIELD_EMAIL    = "email";
    public static final String FIELD_LOGIN    = "login";
    public static final String FIELD_PASSWORD = "password";

    public int getUserID() {
        return userID;
    }

    public void setUserID(int userID) {
        this.userID= userID;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}

Explications :

  • On étend la classe Bean, car la classe WSResult attend des objets Bean dans sa liste.
  • En plus des attributs caractérisant un utilisateur, on ajoute des attributs publics et statiques pour préciser quels champs de la base de données correspondent.
  • Pour que Play! puisse se connecter à une base de données, vous devez procéder à quelques configurations.

Retour à la base

Une fois que la classe UserBean a été créée, il faut écrire dans la classe Application la méthode qui va consulter la base de données et retourner un objet Result :

package controllers;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import models.WSResult;
import models.beans.UserBean;
import play.libs.Json;
import play.mvc.Result;

public class Application extends ApplicationController {

    /**
     * Retourne la liste des utilisateurs de la base de données.
     * 
     * @return Result
     */
    public static Result userList() {

        WSResult result  = new WSResult();
        String queryName = "user_list";

        String query = ApplicationController.getQuery(queryName);

        PreparedStatement statement;
        try {

            statement = ApplicationController.getConnection().prepareStatement(query);
            ResultSet resultSet = statement.executeQuery();

            // Parcours des lignes retournées
            while (resultSet.next()) {

                UserBean bean = new UserBean();
                bean.setId(resultSet.getInt(UserBean.FIELD_ID));
                bean.setEmail(resultSet.getString(UserBean.FIELD_EMAIL));
                bean.setLogin(resultSet.getString(UserBean.FIELD_LOGIN));
                bean.setPassword(resultSet.getString(UserBean.FIELD_PASSWORD));

                result.addBean(bean);
            }

        } catch (SQLException e) {

            result = new WSResult("Erreur SQL.", WSResult.ERROR_CODE_SQL);
            result.setErrorCode(e.getErrorCode());
            result.setErrorMessage(e.getMessage());
        }

        return ok(Json.toJson(result));
    }
}

Explications :

  • La méthode utilise l'api java sql, pour préparer les requêtes et les exécuter.
  • On parcourt ensuite les lignes retournées par la source de données
  • Pour chaque ligne de résultat, on instancie un nouveau Bean (UserBean ici puisqu'il s'agit d'utilisateurs) et on l'ajoute à la liste de Beans de l'objet Result.
  • On retourne l'objet Result transformé en JSON.
  • En cas d'erreur SQL, on retourne un objet Result contenant le message et le code d'erreur SQL.

Stockage des requêtes

Dans la méthode ci-dessus, on ne voit aucun code SQL. Les requêtes sont stockées dans un fichier qui les regroupe toutes : conf/sql.properties.

Comme tout fichier .properties standard, chaque ligne est de la forme clé = valeur :

user_list = SELECT * FROM user
admin_user = SELECT * FROM user WHERE login = 'admin'

Ces requêtes sont récupérées dans les méthodes java à partir de leurs clés :

String queryName = "user_list";
String query = ApplicationController.getQuery(queryName);

Lancement des requêtes

Finalement, si vous tapez http://localhost:9000/userList dans votre navigateur, vous obtenez quelque chose de la forme :

Résultat JSON user list

En résumé :

  • Play! reçoit votre requête HTTP.
  • Il cherche dans son fichier route la méthode correspondante et l'appelle.
  • La méthode cherche la requête SQL demandée, l'exécute et parcours les résultats.
  • Ces résultats sont "transformés en Bean" et stockés dans un objet Result.
  • L'objet Result est transformé en JSON et Play! vous en retourne le flux.

Remarque : Si vous avez un affichage du JSON illisible, il existe une extension Firefox très utile.

Astuce [eZ4] Utiliser eZ Publish avec Play! Framework

Play! Framework

Play! est un framework simple de développement MVC web open-source basé sur Java.

Ce n’est pas un framework Java EE puisqu’il n’implémente pas la norme servlet. Il n’a donc pas besoin de serveur d’application comme Tomcat ou WebSphere et utilise le « serveur http » JBoss Netty permettant de meilleures performances.

Il utilise une architecture stateless ce qui rend son utilisation idéale pour le développement de services web REST.

Pourquoi utiliser Play! ?

Vous pouvez faire à peu près autant de chose avec PHP qu'avec Java, mais si la solution dont vous avez besoin est déjà existante en Java, pourquoi la recréer en PHP ?

Play! étant basé sur Java, vous pouvez l'utiliser comme interface entre une solution Java et votre projet eZ Publish.

L'architecture eZ Publish + Play!

Architecture eZ Publish - Play

EZ Publish communique avec Play! en lui soumettant des requêtes REST via HTTP. Play! lui retourne en réponse les données au format JSON (par défaut). Play! fournit donc des services web utilisables par eZ Publish.

Astuce [eZ4] Les templates des pages d'erreur

Lorsqu'une erreur se produit, eZ Publish affiche une page avec un message d'erreur. Ces pages sont entièrement personnalisables, puisque elles utilisent des templates standards.

Voici la liste des templates de pages d'erreur fournis par défaut dans eZ Publish (dans design/standard/templates/) :

  • error/kernel/1.tpl : Accès à une page avec autorisations insuffisantes. Affiche un message d'erreur plus la mire de login.
  • error/kernel/2.tpl : Accès à un module inconnu.
  • error/kernel/21.tpl : Accès à une vue inconnue.
  • error/kernel/22.tpl : Accès à un module désactivé.
  • error/kernel/3.tpl : Accès à une ressource indisponible ou verrouillée.
  • error/kernel/4.tpl : Accès à un contenu déplacé. Redirige automatiquement vers le nouvel emplacement.
  • error/kernel/5.tpl : Création d'un brouillon dans une langue invalide.

Astuce [eZ4] Modifier le groupe de classes d'une classe de contenu

Si vous avez beaucoup de classes de contenu à créer, ou si vous n'utilisez que très peu
de celles fournies par eZ Publish, vous avez intérêt à créer votre propre groupe de classes.

Pour cela, allez dans Administration > Classes et cliquez sur le bouton Nouveau groupe de classes.

Pour regrouper vos nouvelles classes et les natives dont vous avez besoin (ex : Image, Folder, ...), vous pouvez déplacer ou copier/déplacer les classes de contenu d'un groupe vers un autre.

Pour cela, allez sur la page de détail de la classe que vous voulez copier (ou déplacer), et cliquez sur Le bouton Groupe de classes en haut. En bas de la page apparaît un bloc permettant de choisir le ou les groupes dans lesquels placer la classe de contenu :

Groupes de la classe

Remarque :

Pour être sûr de ne pas dégrader un fonctionnement natif sans le vouloir, il est plus sûr de "copier" les classes natives dans un autre groupe plutôt que de les retirer du groupe initial.

Astuce [eZ4] Afficher l'icône de la classe d'un contenu

Pour rendre le back-office plus simple et plus convivial, eZ Publish permet de définir un icône pour chaque classe de contenu.

Pour afficher ces icônes dans vos template, eZ Publish fournit un opérateur de template : class_icon(). Grâce à lui, vous pouvez simplement insérer l'icône d'une classe de contenu :

{'folder'|class_icon( 'small', "Texte alternatif à l'image" )}

Ce qui affichera Icône de la classe de contenu Dossier.

Remarques :

  • small affiche l'icône en 16x16 px, normal en 32x32 px.
  • Pour afficher l'icône d'un groupe de classes, utiliser l'opérateur de template classgroup_icon().

Erreur [eZ4] Impossible de se déconnecter

Si vous ne parvenez pas à vous déconnecter, ou si vous êtes automatiquement connecté en tant qu'un utilisateur lorsque vous arriver sur une certaine page, vérifiez les points suivants :

  • Vérifiez que l'utilisateur anonyme utilisé par défaut est le bon. Dans site.ini vous devez avoir AnonymousUserID=10 (10 par défaut, ou un autre si vous en avez choisi un autre).
  • Videz les caches.
  • Vérifiez que vous êtes toujours connecté malgré l'appel de la page http://mon_site.com/user/logout.
  • Vérifiez que vous n'avez pas modifié la configuration des droits et rôles récemment.
  • Vérifiez que vous n'avez pas installé une nouvelle extension récemment .
  • Vérifiez que sur la ou les pages qui posent problème, une fonction ne vous connecte pas en tant qu'un autre utilisateur sans vous reconnecter correctement par la suite.

Erreur [eZ4] L'arbre des contenus n'est plus acessible en front-office et en back-office

Si plus aucun contenu ne s'affiche en front-office, seulement le header et les menus, et qu'en back-office l'onglet Contenus n'est plus accessible, il s'agît d'un problème de cache. EZ Publish n'arrive plus à générer le cache.

Première approche

Vérifiez qu'eZ Publish a bien les droits d'écriture sur le répertoire qui contient le cache (var/cache/ par défaut).

Remarque :

Ce problème de droits est fréquent sous linux, mais peut également se produire sous Windows (ex : Windows 7 Professional).

Deuxième approche

Si le problème persiste, essayez la commande suivante à la racine du site, pour vraiment supprimer le cache :

php bin/php/ezcache.php --clear-all --purge

Troisième approche

Supprimez tout le contenu du répertoire var/cache/ à la main.

Quatrième approche

Faîtes le ménage sur le serveur. Il se peut que le disque soit plein.

Astuce [eZ4] Ajouter des boutons à la websitetoolbar

EZ Publish inclut par défaut l'extension ezwt, qui propose une barre d'outils pour l'édition en front-office :

Websitetoolbar

Cette barre d'outils utilise bien sûr des templates, que vous pouvez surcharger. Voici un exemple pour ajouter un bouton de déconnexion à cette barre d'outils.

Surcharger le template existant

Ajoutez la règle de surcharge suivante dans le fichier extension/mon_extension/settings/siteaccess/site/override.ini.append.php :

[website_toolbar]
Source=parts/website_toolbar.tpl
MatchFile=parts/website_toolbar.tpl
Subdir=templates

Copiez le fichier extension/ezwt/design/standard/templates/parts/website_toolbar.tpl dans votre extension (extension/mon_extension/design/mon_design/templates/parts/website_toolbar.tpl).

Ajouter le bouton

Ajouter le code de votre bouton avant le template d'aide :

{* Ajout d'un bouton pour se déconnecter *}
<div id="ezwt-monextensionaction-logout" class="ezwt-actiongroup">
    <a class="logout_button"
       href="{'user/logout'|ezurl( 'no' )}"
       title="{'Me déconnecter'|i18n( 'user/login' )}">
        <img src="{'global/picto_logout.png'|ezimage( 'no' )}" alt="" />
    </a>
</div>

{include uri='design:parts/websitetoolbar/help.tpl'}

Remarques :

  • Le div conteneur, avec la classe ezwt-actiongroup définit un nouveau groupe de boutons.
  • Vous pouvez choisir à quel endroit de la barre votre bouton doit apparaître (ici à la fin), et vous pouvez ajouter un bouton à un groupe déjà existant.

Résultat :

Websitetoolbar avec déconnexion

Astuce [eZ4] Utiliser le layout d'impression

Il est courant de proposer une fonction d'impression pour les articles de son site.

Cette fonction peut appeler simplement la fonction Imprimer du navigateur en javascript (window.print()), ou définir un affichage de l'article épuré. EZ Publish propose un système de layout pour définir plusieurs gabarits d'affichage. Celui d'impression notamment, qui permet d'afficher un article sans les menus, le header et le footer.

Alors que layout principal utilise le template pagelayout.tpl partout dans le site, celui d'impression utilise print_pagelayout.tpl.

Affichage en mode page, avec le layout principal :

Affichage en mode page, avec le layout principal

Affichage en mode impression, avec le layout d'impression :

Affichage en mode impression, avec le layout d'impression

Comment afficher un article avec ce layout épuré ?

Appelez simplement l'article avec une URL de type : http://mon_site.com/layout/set/print/content/view/full/<node_id>.

Remarques :

  • Vous pouvez définir d'autres layout, par exemple pour afficher un contenu sous forme xml ou json. Le template devra toujours avoir pour nom nomdulayout_pagelayout.tpl, et l'url la forme http://mon_site.com/layout/set/<nomdulayout>/content/view/full/<node_id>. Dans ce cas, déclarez votre nouveau layout dans le fichier layout.ini.
  • Vous pouvez appeler d'autres vues que content/view. Il suffit de les faire précéder par /layout/set/<nomdulayout>/.

Erreur [eZ4] data error dans le back-office, au chargement des sous-éléments

Lorsque vous chargez une page de back-office affichant l'arbre des contenus à gauche, ou le tableau des sous-éléments, il arrive parfois que le chargement échoue.

Data error en BO

Ce chargement est effectué par un appel AJAX, et il fonctionne mal si vous utilisez le mode_cgi d'apache.

Si cette erreur se produit trop souvent (notamment en développement), vous pouvez désactiver le mode cgi.

Sous Windows, si vous utilisez Wamp, il suffit de décocher cgi_module dans apache > Modules Apache, ou de modifier votre httpd.conf et commenter la ligne correspondante.

Sous Debian, vous pouvez le faire en ligne de commande :

sudo a2dismod cgi
sudo service apache2 reload

Astuce [eZ4] Renommer les custom tag de l'editeur WYSIWYG

Par défaut, eZ Publish propose des tags personnalisés dans son éditeur WYSIWYG (eZ Online Editor ou ezoe). Si les termes quote, underline ou autres ne vous conviennent pas, vous pouvez les renommer dans le fichier content.ini :

Par exemple, dans le fichier monextension/settings/content.ini.append.php :

<?php /* #?ini charset="utf-8"?

[CustomTagSettings]
AvailableCustomTags[]=quote
CustomTagsDescription[quote]=Citation

*/ ?>
  • AvailableCustomTags est la liste des tags personnalisés qui seront disponibles dans l'éditeur
  • CustomTagsDescription est la liste des libellés qu'auront ces tags

Dans l'exemple ci-dessus, le tag personnalisé quote, sera nommé Citation dans la liste en BO.

Astuce [eZ4] Modifier automatiquement les droits sur les fichiers eZ Publish sous linux

Plutôt que d'exécuter des commandes de type chmod pour modifier les droits de lecture/écriture et exécution sur les différents fichiers d'eZ Publish, vous pouvez simplement exécuter un fichier bash fourni par eZ : modfix.sh.

A la racine du projet, lancez la commande suivante :

./bin/modfix.sh 

Astuce Paramétrer les tâches planifiées

Pour fonctionner correctement, eZ Publish à besoin que des scripts soient exécutés à intervalles réguliers. Ces scripts permettent de supprimer les brouillons, effectuer les opérations de workflow, mettre à jours alias d'url des contenus, indexer les contenus pour la recherche, ...

Sous linux, la définition des tâches planifiées est stockée dans des crontab. Ces fichiers définissent quelles commandes doivent être exécutées, par quel utilisateur et à quel intervalle.

EZ Publish 4 fournit une crontab, qui peut être utilisée comme base pour votre installation. Il s'agit du fichier ezpublish.cron, à la racine.

Pour eZ Publish 4

# Chemin absolu vers la racine de votre projet (à modifier).
EZPUBLISHROOT=/path/to/the/ez/publish/directory

# Emplacement de l'exécutable PHP en mode ligne de commande (à modifier si besoin).
PHP=/usr/local/bin/php

# Liste des cronjobs principaux à exécuter
# à 5h00 tous les matins
0 5 * * * cd $EZPUBLISHROOT && $PHP runcronjobs.php -q 2>&1

# Liste des cronjobs "infrequent" d'eZ Publish
# à 4h20 tous les dimanches matin
20 4 * * 7 cd $EZPUBLISHROOT && $PHP runcronjobs.php -q infrequent 2>&1

# Liste des cronjobs "frequent" d'eZ Publish
# tous 15 minutes
0,15,30,45 * * * * cd $EZPUBLISHROOT && $PHP runcronjobs.php -q frequent 2>&1

Pour eZ Publish 5

A vous de créer le fichier ezpublish.cron à la racine, cat celui présent dans le sous-répertoire ezpublih_legacy/ n'est pas à jour.

# Chemin absolu vers la racine de votre projet (à modifier).
EZPUBLISHROOT=/path/to/the/ez/publish/directory

# Emplacement de l'exécutable PHP en mode ligne de commande (à modifier si besoin).
PHP=/usr/local/bin/php

# Environnement (prod ou dev)
ENV=prod

# Liste des cronjobs principaux à exécuter
# à 5h00 tous les matins
0 5 * * * cd $EZPUBLISHROOT && $PHP ezpublish/console --env=$ENV ezpublish:legacy:script runcronjobs.php -q 2>&1

# Liste des cronjobs "infrequent" d'eZ Publish
# à 4h20 tous les dimanches matin
20 4 * * 7 cd $EZPUBLISHROOT && $PHP ezpublish/console --env=$ENV ezpublish:legacy:script runcronjobs.php -q infrequent 2>&1

# Liste des cronjobs "frequent" d'eZ Publish
# tous 15 minutes
0,15,30,45 * * * * cd $EZPUBLISHROOT && $PHP ezpublish/console --env=$ENV ezpublish:legacy:script runcronjobs.php -q frequent 2>&1

Exécution

Pour que cette crontab soit utilisée par votre système, exécutez la commande suivante, en remplaçant le chemin par celui de votre projet :

crontab /var/www/mon_projet/ezpublish.cron
# Vérification
crontab -l

Astuce [eZ4] Activer la publication / dépublication automatique de vos contenus

Par défaut eZ Publish propose les champs Date de publication et Date de dépublication pour la classe de contenu Article, mais rien n'est automatisé.

La publication et la dépublication sont deux éléments différents et chacun d'eux doit être activé de manière indépendante dans eZ Publish.

Publication automatique

Pour cela on utilise le système de workflow d'eZ Publish :

  • Allez dans l'onglet Administration du back-office, puis sur Workflows.
  • Cliquez sur le groupe de workflow Standard, ou créez-en un nouveau pour y ranger votre workflow.
  • Cliquez sur le bouton Nouveau processus de workflow.
  • Donnez-lui un nom et créez un évènement de type Évènement / Attendre jusqu'à.

Vous devez maintenant informer eZ Publish des classes de contenu qui contiennent un champ date indiquant quand publier le contenu. Par exemple le champ Date de publication pour la classe de contenu Article.

Pour cela :

  • Sélectionnez la classe de contenu concernée (ex: Article).
  • Cliquez sur le bouton Mettre à jour les champs.
  • Sélectionnez le champ date qui servira de référence (ex: Date de publication).
  • Cliquez sur le bouton Sélectionner l'attribut.
  • Validez

Vous venez donc de créer un workflow qui vérifie la date de publication d'un contenu et décide s'il doit être publié. Ce n'est pas fini, il faut maintenant demander à eZ publish de déclencher ce workflow avant chaque publication d'un contenu. Pour cela :

  • Toujours dans l'administration, allez dans Déclencheurs.
  • Affectez votre nouveau workflow à l'évènement content publish before, c'est à dire "avant la publication".
  • Validez pour Appliquer les changements.

La publication automatique à la date souhaitée est maintenant effective.

Remarques :

  • Vous pouvez configurer le workflow pour plusieurs classes de contenu en même temps
  • Les traitement exécutés par le workflow sont exécutés via des cronjobs fournis par eZ Publish. Pour que la publication fonctionne, le cronjob workflow doit obligatoirement être exécuté. (Plus d'informations sur les cronjobs.)

Dépublication automatique

La dépublication automatique est réalisée via le script unpublish.php fourni par eZ Publish. Vous avez seulement deux choses à effectuer pour mettre en place cette fonctionalité :

  • Activez le cronjob principal, qui exécute entre autres le script unpublish.php. Pour cela, vous devez planifier l'exécution de la commande suivante au niveau du système, au moins un fois par jour :
php runcronjobs.php -q 2>&1
`

* Définissez les classes de contenu contenant un attribut de type Date, 
dont le nom technique est `unpublish_date`. Pour cela, vous devez surcharger le fichier `content.ini`.

Par exemple, dans le fichier `monextension/settings/content.ini.append.php` :

```ini
<?php /* #?ini charset="utf-8"?

[UnpublishSettings]
# Noeuds racines des arborescences pour lesquelles appliquer la dépublication
RootNodeList[]=2
 # Liste des ID des classes de contenu à prendre en compte
ClassList[]=46

*/ ?>

Explications :

  • La dépublication ne concernera que les contenus de l'abre des contenus (càd ni les utilisateurs, ni ce qu'il y a dans la médiathèque)
  • La dépublication ne concernera que les contenus de type Article (dont l'ID est par défaut 46)

Astuce [eZ4] ID des noeuds racines du site

Par défaut, eZ Publish initialise les arbres des contenus, des utilisateurs, des médias, ... avec des nœuds racines spécifiques. Cette configuration est spécifiée dans le fichier content.ini. Elle peut donc être surchargée.

Voici la configuration par défaut :

[NodeSettings]
# Le node ID du noeud racine de l'arbre des contenus
RootNode=2
# Le node ID du noeud racine de l'arbre des utilisateurs
UserRootNode=5
# Le node ID du noeud racine de la médiathèque
MediaRootNode=43
# Le node ID du noeud racine de l'arbre de configuration
SetupRootNode=48
# Le node ID du noeud racine de l'arbre de design
DesignRootNode=58

Remarques :

  • Les deux derniers arbres sont dépréciés et masqués par défaut dans eZ Publish.
  • Même sans avoir besoin de les modifier, il est intéressant connaître ces valeurs si on en a besoin lors d'un fetch depuis l'une de ces racines. On peut même envisager de récupérer la valeur via ezini() plutôt que de l'utiliser en dur.

Erreur [eZ4] Le front-office n'est plus accessible aux utilisateurs anonymes

Vous souhaitez autoriser l'accès au front-office aux utilisateurs non connectés, mais suite à une modification des droits et rôles, le front-office n'est plus accessible ?

Message d'erreur :

Accès refusé
Vous n'avez pas le droit d'accéder à cette zone.

Lorsque vous modifiez les politiques de sécurité du rôle Anonymous, veillez bien à laisser le droit d'accès à la vue user/login, pour le siteaccess du front-office. Sans ça, les utilisateurs du site n'auront plus accès au front-office.

Astuce [eZ4] Mettre en place un workflow de validation

EZ Publish propose une interface de création de workflow, et notamment de validation. Cela permet de définir des étapes de validation lors de la publication d'un contenu.

L'exemple le plus courant est le cas où un rédacteur soumet un article sur le site et attend sa validation par le responsable communication du site. C'est seulement une fois validé que l'article apparaitra publiquement sur le site.

Voici les étapes à suivre pour mettre en place un tel workflow.

Créer un processus de workflow générique

  • Rendez-vous dans le back-office du site, dans l'onglet Administration puis sur la page Workflows
  • Cliquez sur le groupe standard ou créez un nouveau groupe de workflow. (Le groupe standard est largement suffisant sauf si vous avez des dizaines de workflow différents)
  • Créez un Nouveau processus de workflow nommé par exemple Workflow de validation générique.
  • Ajoutez un évènement de type approbation (Évènement / Approuver)
  • Choisissez maintenant les éléments gérés par le workflow validation. Vous pouvez choisir :
    • les sections pour lesquelles le workflow s'appliquera (À priori Standard et/ou Média)
    • les langues concernées
    • si le workflow agit pour la création, la modification de l'objet, ou les deux
    • quels utilisateurs et groupes d'utilisateurs pourront valider les contenus avant leur publication
    • quels groupes d'utilisateurs pourront publier leur contenu sans validation

Remarque :

Il est conseillé de choisir les options les plus larges possible (ex: pour toutes les langues, pourr la création et la modification, ...). Vous pourrez affiner votre configuration par la suite (voir la suites).

Créer un processus de workflow spécifique

Vous venez de créer un processus de workflow générique, qui sera appliqué sur tous les contenus, sans distinction. Ce n'est pas vraiment utilisable tel quel.

On souhaite en général préciser que ça ne doit concerner que telle ou telle classe de contenu. Pour cela, on utilise des multiplexeurs. Les multiplexeurs sont aussi des processus de workflow, mais avec des options différentes des précédentes.

  • Créez un Nouveau processus de workflow nommé par exemple Workflow de validation des articles.
  • Ajoutez un évènement de type multiplexer (Évènement / Multiplexer)
  • Choisissez maintenant les éléments gérés par le workflow de validation. Vous pouvez choisir :
    • les sections pour lesquelles le workflow s'appliquera (À priori Standard et/ou Média, ou un section que vous avez créée)
    • les langues concernées
    • les classes de contenus concernées
    • si le workflow agit pour la création, la modification de l'objet, ou les deux
    • les groupes d'utilisateurs non concernés par la validation
    • le processus de workflow à lancer (ex: Workflow de validation générique)

Définir quand déclencher le workflow

Votre processus workflow est maintenant créé, mais il vous reste encore à indiquer à eZ Publish à quel moment il doit se déclencher. Pour cela :

  • Cliquez sur Déclencheurs dans le menu de gauche
  • Choisissez content publish before, pour que le processus de validation soit effectué avant la publication et affectez-y votre nouveau processus de workflow (choisissez celui avec le multiplexeur, pas le générique).

Mettre en place le cronjob workflow

Les étapes sont terminées côté administration, il reste cependant une dernière chose à mettre en place : le cronjob workflow.

Si vous êtes sous Linux/Unix, vous devez mettre à jour votre crontab pour que le cronjob soit exécuté à intervalle régulier :

EZPUBLISHROOT=/chemin/vers/ezpublish/mon_site
PHP=/chemin/vers/le/binaire/php
0,15,30,45 * * * *      cd $EZPUBLISHROOT; $PHP runcronjobs.php -q 2>&1 

Explications :

  • EZPUBLISHROOT indique le chemin absolu vers la racine de votre site,
  • PHP le chemin vers le binaire php
  • la dernière ligne que le fichier runcronjobs.php devra être exécuter tous les quarts d'heure.

Ce fichier va lancer les différents cronjobs de votre site, définis dans cronjob.ini. Vérifiez donc que dans ce fichier de configuration, vous ayez ce paramétrage :

<?php /* #?ini charset="utf-8"?

[CronjobSettings]
ScriptDirectories[]=cronjobs
Scripts[]=workflow.php 

*/ ?>

Astuce [eZ4] Créer une validation de champ spécifique pour l'édition de contenus

Lorsqu'un contenu est créé, eZ Publish vérifie la validité des valeurs envoyées. Pour un champ de type "Nombre entier" par exemple, il vérifie qu'on a bien un nombre et pas des lettres pour valeur.

Ces vérifications sont cependant limitées et il peut être intéressant d'en ajouter. On peut imaginer par exemple une classe de contenu Contrat, ayant un champ numéro de la forme XX-YYYY, avec XX deux lettres et YYYY quatre chiffres. Vous pouvez effectuer une vérification sur ce champ, en étendant la classe eZContentObjectEditHandler d'eZ Publish.

Pour cela, vous devez déclarer votre extension comme possédant un Content Edit Handler dans content.ini :

Par exemple, dans le fichier monextension/settings/content.ini.append.php :

<?php /* #?ini charset="utf-8"?

[EditSettings]
ExtensionDirectories[]=monpremier

*/ ?>

Avec cette configuration, eZ Publish va chercher le fichier extension/monextension/content/monpremierhandler.php.

Créez le répertoire content/ et le fichier monpremierhandler.php. Ce fichier doit contenir une classe qui étend eZContentObjectEditHandler.

Voici maintenant un exemple simple pour vérifier que le champ contract_number d'un contenu de type contract commence bien par un c :

<?php
class MonPremierHandler extends eZContentObjectEditHandler {

    /**
     * Effectue des opérations au moment de la soumission du formulaire de la vue /content/edit, après la vérification des champs.
     */
    function fetchInput( $http, &$module, &$class, $object, &$version, $contentObjectAttributes, $editVersion, $editLanguage, $fromLanguage ) {
    }

    /**
     * Retourne la liste des paramètres POST HTTP qui déclencheront une action.
     * (Retourne un tableau vide par défaut.)
     */
    static function storeActionList() {

        return array();
    }

    /**
     * Effectue des opérations au moment de la publication de l'objet.
     *
     * Rq : L'objet a déjà été affecté à un noeud à ce moment.
     *
     * @param int $contentObjectID
     * @param eZContentObjectVersion $contentObjectVersion
     */
    function publish( $contentObjectID, $contentObjectVersion ) {
    }

    /**
     * Effectue des vérifications supplémentaires sur les champs soumis à l'édition d'un contenu.
     *
     * @return array
     */
    function validateInput( $http, &$module, &$class, $object, &$version, $contentObjectAttributes, $editVersion, $editLanguage, $fromLanguage, $validationParameters ) {

        $result = array( 'is_valid' => true, 'warnings' => array() );

        if ( $class->Identifier == 'contract' ) {

            // Récupération de la liste des attributs modifiés
            $contentObjectAttributes = $object->contentObjectAttributes();

            // Recherche de l'attribut 'contract_number'
            foreach ( $contentObjectAttributes as $contentObjectAttribute ) {

                if ( $contentObjectAttribute->contentClassAttributeIdentifier() == 'contract_number' ) {

                    // Récupération du numéro de contrat envoyé
                    $postValues     = $http->attribute('post');
                    $contractNumber = $postValues['ContentObjectAttribute_' . $contentObjectAttribute->DataTypeString . '_data_text_' . $contentObjectAttribute->ID];

                    // Si le numéro de contrat ne commmence pas par 'c'
                    if ( strpos( $contractNumber, 'c' ) !== 0 ) {

                        $result['warnings'][] = array( 
                            'text' => ezpI18n::tr( 
                                'extension/monextension/validation', 
                                "The contract number should begin with a 'c'." 
                            ) 
                        );
                        $result['is_valid']   = false;
                    }
                }
            }
        }

        return $result;
    }
}
?>

La classe eZContentObjectEditHandler possédant 3 méthodes abstraites, vous devez les implémentez dans votre classe (même si vous les laissez vides) : fetchInput(), storeActionList() et publish().

Une quatrième méthode va nous intéresser : validateInput(). C'est elle qui permet d'ajouter une validation personnalisée.

Elle possède les paramètres suivants :

  • $http : les éléments soumis par le formulaire et les variables de session.
  • $module : le module courant (= content).
  • $class : une instance de la classe de contenu de l'objet édité.
  • $object : l'objet modifié.
  • $contentObjectAttributes : le tableau des attributs de l'objet, avant modification.
  • $version : le numéro de version de l'objet.

Par défaut, cette méthode retourne un tableau d'erreur vide :

$result = array( 'is_valid' => true, 'warnings' => array() );

Pour chaque champ vérifié, en cas d'erreur, on stocke le message d'erreur à afficher. Ex :

$result['warnings'][] = array( 
    'text' => ezpI18n::tr( 
        'extension/monextension/validation', 
        'The contract number should begin with \'c\'.'
    ) 
);
$result['is_valid']   = false;

Astuce [eZ4] Créer un Content Edit Handler personnalisé

Pour ajouter des traitements spécifiques lors de la création ou la modification d'objets, vous pouvez créer un Content Edit Handler personnalisé. On peut par exemple créer automatiquement un nouvel article souhaitant la bienvenue à un utilisateur lors de son inscription.

Configuration

Pour cela, vous devez déclarer votre extension comme possédant un Content Edit Handler dans content.ini :

Par exemple, dans le fichier monextension/settings/content.ini.append.php :

<?php /* #?ini charset="utf-8"?

[EditSettings]
ExtensionDirectories[]=monpremier

*/ ?>

Avec cette configuration, eZ Publish va chercher le fichier extension/monextension/content/monpremierhandler.php.

Content Edit Handler

Créez le répertoire content/ et le fichier monpremierhandler.php. Ce fichier doit contenir une classe, qui étend eZContentObjectEditHandler.

Pour qu'eZ Publish prenne en compte cette classe, videz les caches et régénérer les autoloads.

Voici maintenant un exemple simple pour loguer le nom d'un utilisateur lorsqu'il est publié :

<?php
class MonToutPremierHandler extends eZContentObjectEditHandler {

    /**
     * Effectue des opérations au moment de la soumission du formulaire de la vue /content/edit, après la vérification des champs.
     */
    function fetchInput( $http, &$module, &$class, $object, &$version, $contentObjectAttributes, $editVersion, $editLanguage, $fromLanguage ) {

        // Exemples :

        // Si l'action courante est l'enregistrement en base
        // (liste des actions disponibles dans kernel/content/module.php)
        if( $module->isCurrentAction( 'Store' ) ) {

            // Traitement ...
        }

        // Si un input "Name" a été soumis
        if( $http->hasPostVariable( 'Name' ) ) {

            // Traitement ...
        }
    }

    /**
     * Retourne la liste des paramètres POST HTTP qui déclencheront une action.
     * (Retourne un tableau vide par défaut.)
     */
    static function storeActionList() {

        return array();
    }

    /**
     * Effectue des opérations au moment de la publication de l'objet.
     *
     * Rq : L'objet a déjà été affecté à un noeud à ce moment.
     *
     * @param int $contentObjectID
     * @param eZContentObjectVersion $contentObjectVersion
     */
    function publish( $contentObjectID, $contentObjectVersion ) {

        // Exemple : On logue le nom d'un utilisateur lorsqu'il s'inscrit.

        // Récupération de l'objet publié
        $object = eZContentObject::fetch( $contentObjectID );

        // Récupération de la classe de l'objet
        $contentClass = $object->attribute('content_class');

        // Si l'objet est de type utilisateur et qu'il vient d'être créé
        if ( $contentClass->Identifier == 'user' && $contentObjectVersion == 1 ) {

            $message = 'Nouvel utilisateur inscrit : ' . $object->Name;
            eZLog::write( $message );
        }
    }
}
?>

La classe eZContentObjectEditHandler possédant 3 méthodes abstraites, vous devez les implémentez dans votre classe.

fetchInput() :

Cette méthode permet d'effectuer des traitements au moment où le formulaire de la vue content/edit/ est soumis. Les paramètres de la fonction permettent d'effectuer des traitements différents selon l'action en cours, les valeurs postées, l'objet édité, ...

storeActionList() :

Cette méthode retourne la liste des paramètres POST HTTP qui déclencheront une action, ou un tableau vide s'il n'y en a pas.

publish() :

Cette méthode permet d'effectuer des traitements au moment où l'objet est publié pour la première fois, ou lorsqu'une nouvelle version est publiée. L'objet a déjà été affecté à un nœud quand cette méthode est exécutée.

Remarque :

Vous pouvez également surcharger la méthode validateInput(), pour effectuer des vérification complémentaires sur les champs de l'objet. (Voir l'article [eZ4] Créer une validation de champ spécifique pour l'édition de contenus.)

Astuce [eZ4] Utiliser l'ActionHandler d'eZ Publish

Les actions dans eZ Publish

Tout d'abord, qu'est-ce qu'une action dans eZ Publish ?

Il s'agit d'un traitement effectué à la soumission d'un formulaire pointant vers content/action.

Exemple :

<form method="post" action="{'content/action'|ezurl( 'no' )}">
    <label for="message">{'Message'|i18n( 'sample' )} :</label>
    <textarea id="message" name="message"></textarea>

    <input type="hidden" name="ContentObjectID" value="{$node.object.id}" />
    <input type="hidden" name="NodeID" value="{$node.node_id}" />
    <input type="submit" name="LogMessageButton" value="Log" />
</form>

Le formulaire est constitué d'un textarea permettant à l'utilisateur de saisir son message, d'un bouton submit et de champs cachés. Notez que le champ ContentObjectID est obligatoire, même si l'action n'utilise pas sa valeur (vous pouvez retourner -1 dans ce cas).

Le nom du submit correspond au nom de l'action à effectuer. EZ Publish référence déjà beaucoup d'actions (notamment toutes celles relatives aux boutons submit du back-office), mais vous pouvez en rajouter.

Créer ses propres actions

Supposons qu'on veuille loguer le message du textarea et le node_id de la page à la soumission du formulaire.

Pour qu'eZ Publish trouve vos actions, vous devez déclarer votre extension comme ayant des actions, dans le fichier site.ini.

Par exemple dans extension/monextension/settings/site.ini.append.php :

<?php /* #?ini charset="utf-8"?

[ActionSettings]
ExtensionDirectories[]=monextension

*/ ?>

Dans votre extension, créez un répertoire actions/, contenant le fichier content_actionhandler.php :

<?php
include_once( 'lib/ezutils/classes/ezoperationhandler.php' );

function monextension_ContentActionHandler( &$module, $http, $objectID ) {

    // Action de loguer un message
    if ( $http->hasPostVariable( 'LogMessageButton' ) ) {
        // Si le formulaire soumis contient un message non vide
        if ( $http->hasPostvariable( 'message' ) && $http->postvariable( 'message' ) != '' ) {
            $message = $http->postvariable( 'message' );
            $nodeID  = $http->postvariable( 'NodeID' );

            $logMessage = 'La page ' . $nodeID . ' a été affichée et on a soumis le message : ' . $message;
            eZLog::write( $logMessage );
        }

        // Redirection vers la page du formulaire
        $module->redirectTo( '/content/view/full/' . $nodeID );
        return;
    }
}
?>

Explications :

  • Ce fichier contient une fonction dont le nom est obligatoirement préfixé par le celui de votre extension.
  • Toutes les actions sont listées, soit par une suite de if, soit dans un switch, en fonction du nom de l'action envoyé par le formulaire (LogMessageButton dans cet exemple).
  • Chaque action redirige vers une page, ou vers la vue d'un module.

Remarque :

N'oubliez pas de vider les caches et régénérer les autoloads pour qu'eZ Publish trouve votre nouvel ActionHandler.

Astuce [eZ4] Créer un opérateur de template

Les opérateurs de template sont des fonctions utilisables dans les template, qui pointent vers des fonctions PHP. EZ Publish en fournit plusieurs dizaines, comme explode(), fetch(), i18n(), ...

Voici comment créer vos propres opérateurs, effectuant tel ou tel traitement, et retournant une valeur de retour.

MonAPI

Dans cet exemple on supposera qu'on possède la classe MonAPI, contenant des méthodes PHP "utiles" à notre projet :

/**
 * Classe contenant les méthodes PHP utiles au projet.
 *
 * Rq : Tout nouvel opérateur doit également être référencé 
 * dans le fichier eztemplateautoload.php.
 */
class MonAPI {

    /**
     * Méthode retournant "Hello world XX YY" avec XX remplacé par $who et YY remplacé par $suffix.
     * 
     * @param string $who A qui dire bonjour
     * @param string $suffix Ponctuation de fin de bonjour
     */
    public static function sayHelloWorld( $who, $suffix ) {
        return 'Hello world ' . $who . ' ' . $suffix;
    }

    /**
     * Méthode retournant "Goodbye" autant de fois que $nb
     * 
     * @param int $nb Nombre de fois à dire au revoir
     */
    public static function sayGoodbye( $nb ) {

        $goodbye = '';

        for ( $i=0; $i < $nb; $i++ ) {
            $goodbye .= 'Goodbye';
        }

        return $goodbye;
    }
}

Les méthodes ci-dessus étant indispensables et pratiques, vous souhaitez pouvoir les utiliser directement dans vos templates.

Pour cela, vous devez avoir créé et activé votre extension. Dans le répertoire de l'extension, créez un dossier autoloads/, qui contiendra vos opérateurs de template.

Configuration

Pour qu'eZ Publish charge les classes PHP contenues dans ce dossier, vous devez déclarer votre extension comme ayant un dossier autoloads/, dans le fichier site.ini.append.php de votre extension.

Par exemple dans extension/monextension/settings/site.ini.append.php :

<?php /* #?ini charset="utf-8"?

[TemplateSettings]
ExtensionAutoloadPath[]=monextension

*/ ?>

EZ Publish va maintenant chercher le fichier extension/monextension/autoloads/eztemplateautoload.php. Créez donc ce fichier de la forme :

$eZTemplateOperatorArray = array();

$eZTemplateOperatorArray[] = array(
    'class' => 'MaClasseTemplateOperators',
    'operator_names' => array( 'say_hello_world', 'say_goodbye' )
);

Explications :

  • Vous déclarez dans un tableau toutes les classes PHP contenant vos opérateurs de template. Dans cet exemple on a seulement la classe MaClasseTemplateOperators (garder "TemplateOperators" à la fin du nom de la classe permet d'identifier rapidement son utilité).
  • Vous devez indiquer son nom et la liste des opérateurs qu'elle contient.

MaClasseTemplateOperators

Dans le fichier MaClasseTemplateOperators.php, on aura :

/**
 * Classe contenant différents opérateurs de templates de test.
 *
 * Rq : Tout nouvel opérateur doit également être référencé dans le fichier eztemplateautoload.php
 */
class MaClasseTemplateOperators {

    function MaClasseTemplateOperators() {
    }

    function operatorList() {
        return array( 'say_hello_world', 'say_goodbye' );
    }

    function namedParameterPerOperator() {
        return true;
    }

    function namedParameterList() {
        return array(
            'say_hello_world' => array(
                'who' => array( 
                    'type' => 'string',
                    'required' => true,
                    'default' => 'everybody' 
                ),
                'suffix' => array( 
                    'type' => 'string',
                    'required' => true,
                    'default' => '!'
                )
            ),
            'say_goodbye' => array(
                'nb' => array( 
                    'type' => 'int',
                    'required' => true,
                    'default' => 10
                )
            ),
        );
    }

    function modify( $tpl, $operatorName, $operatorParameters, &$rootNamespace, &$currentNamespace, &$operatorValue, &$namedParameters ) {
        switch ( $operatorName )
        {
            case 'say_hello_world':
            {
                $who           = $namedParameters['who'];
                $suffix        = $namedParameters['suffix'];
                $operatorValue = MonAPI::sayHelloWorld( $who, $suffix );
            }
            break;

            case 'say_goodbye':
            {
                $nb            = $namedParameters['nb'];
                $operatorValue = MonAPI::sayGoodbye( $nb );
            }
            break;
        }
    }
}

Explication :

Cette classe contient 3 méthodes importantes :

  • operatorList() : C'est la liste des noms de vos opérateurs.
  • namedParameterList() : C'est la liste des paramètres que peuvent recevoir vos opérateurs. Pour chacun d'eux, vous indiquer le nom et le type du paramètre, ainsi que sa valeur par défaut et s'il est obligatoire.
  • modify() : Cette méthode va être utilisée par eZ Publish lorsqu'il parsera votre template. C'est elle qui va appeler la "vraie" méthode PHP (de la classe MonAPI) avec les paramètres utilisés dans le template. et qui va retourner le résultat au template.

Remarque :

Vous ne devez ni modifier la signature de la méthode modify(), ni le nom de la variable $operatorValue, sans quoi le template operator ne fonctionnera plus.

Pour résumer

Dans votre extension vous devez avoir :

- monextension
    - autoloads
        - eztemplateautoload.php
        - MaClasseTemplateOperators.php
    - classes
        - MonAPI.php
    - settings
        - site.ini.append.php

Vous devez également régénérer les autoloads, pour qu'eZ Publish trouve vos nouvelles classes :

  • soit dans le back-office, dans Administration > Extensions > Regénérer le tableau de chargement des classes des extensions
  • soit en ligne de commande à la racine du projet :
php bin/php/ezpgenerateautoloads.php

Utilisation

C'est terminé, vous pouvez désormais profiter de ces précieux opérateurs. Par exemple, dans un template :

<p class"message1">
    {say_hello_world( 'John Doe', '?' )}
</p>
<p class"message2">
    {say_goodbye( 4 )}
</p>

Astuce [eZ4] Trier les résultats d'un fetch selon l'ordre défini en back-office

Dans le back-office d'eZ Publish, vous pouvez définir l'ordre dans lequel doivent être listés les enfants d'un nœud. Pour cela, cliquer sur l'onglet classement, et choisissez la méthode et le sens du tri à appliquer.

Pour que ce tri soit appliqué également en front-office, utilisez le paramétrage suivant dans votre template :

{def $node_list = fetch( 'content', 'list', 
    hash( 
        'parent_node_id', $node.node_id,
        'sort_by', $node.sort_array 
    ) 
)}

Explication :

Le type de tri configuré en BO est récupérable à partir du nœud via $node.sort_array.

Astuce [eZ4] Régénérer les alias d'URL

Lorsque vous déplacez ou renommez des nœuds, eZ Publish ne modifie pas automatiquement leurs alias d'url, pour éviter de briser les liens existants.

Pour forcer la mise à jour de l'url, vous pouvez appeler le script bin/php/updateniceurls.php depuis la racine de votre site :

php bin/php/updateniceurls.php

Erreur [eZ4] Error ocurred using URI: /stylesheets/t2/classes-colors.css

Il peut arriver de trouver ce message dans error.log :

[ May 22 2012 12:44:51 ] [127.0.0.1] index:
Undefined module: stylesheets
[ May 22 2012 12:44:51 ] [127.0.0.1] error/view.php:
Error ocurred using URI: /stylesheets/t02/site-colors.css
[ May 22 2012 12:44:51 ] [127.0.0.1] index:
Undefined module: stylesheets
[ May 22 2012 12:44:52 ] [127.0.0.1] error/view.php:
Error ocurred using URI: /stylesheets/t02/classes-colors.css

Ce message est dû à deux fichiers css qui ne sont pas trouvés par eZ Publish.

Dans les fichiers design.ini et page_head_style.tpl, est définit par défaut un fichier css à inclure :

<?php /* #?ini charset="utf-8"?

[StylesheetSettings]
SiteCSS=stylesheets/t02/site-colors.css
ClassesCSS=stylesheets/t02/classes-colors.css

*/ ?>

Ces fichiers n'existent pas, car le dossier n'est pas t02/ mais t2/ et parce qu'il manque le chemin depuis la racine du site (design/base/).

Si vous voulez éviter qu'eZ Publish essaie d'inclure ces fichiers css, il suffit de surcharger le fichier page_head_style.tpl ou le fichier design.ini. La deuxième solution est la plus simple.

Ajoutez donc ça dans monextension/settings/design.ini.append.php :

<?php /* #?ini charset="utf-8"?

[StylesheetSettings]
ClassesCSS=
SiteCSS=

*/ ?>

Astuce [eZ4] Passer des paramètres à une vue via l'url

En PHP standard, lorsqu'on veut passer des paramètres à une page via l'url, on utilise quelque chose de la forme http://monsite/?param1_name=param1_value&param2_name=param2_value.

EZ Publish masque ce fonctionnement lors de l'utilisation des modules et des vues. Il différencie deux types de paramètres :

  • ordonnées
  • non ordonnés

Exemple d'URL :

http://monsite/index.php/mon_module/ma_vue/value1/(param3url)/value3/(param2url)/value2

Dans cet exemple on a 3 paramètres :

  • param1, qui est un paramètre ordonné
  • param2url et param3url qui sont non ordonnés

Le param1 étant ordonné, vous n'avez pas besoin de le nommer. En revanche, param2url et param3url doivent être précisés entre parenthèses avant leurs valeurs respectives.

Ce fonctionnement n'est pas automatique, vous devez informer eZ Publish des paramètres susceptibles d'être passés à votre vue, grâce au fichier module.php.

Par exemple, dans le fichier monextension/modules/monmodule/module.php :

<?php
$ViewList = array();
$ViewList['sample'] = array(
    'functions' => array( 'sample' ),
    'script' => 'sample.php',
    'params' => array( 'param1' )
    'unordered_params' => array( 
        'param2url' => 'param2',
        'param3url' => 'param3' 
    )
);

$FunctionList['sample'] = array();

Pour l'URL d'exemple et cette configuration, on aura dans mon fichier de vue mavue.php :

<?php
echo $Params['param1']; // Affiche 'value1'
echo $Params['param2']; // Affiche 'value2'
echo $Params['param3']; // Affiche 'value3'

Pour les paramètres non ordonnés param2url et param3url, on a spécifié dans le fichier module.php que leur valeurs seraient disponibles avec les clés param2 et param3.

Pour param1 qui est ordonné, la clé param1 est disponible sans être renommée.

Astuce [eZ4] Créer un cronjob

Pour effectuer une tâche à intervalle régulier, eZ Publish propose un système de scripts : des cronjobs.

Pour créer un cronjob, vous devez avoir créé et activé votre extension. Dans le répertoire de l'extension, créez un répertoire cronjobs/, qui contiendra vos scripts.

Script

Voici un exemple simple d'un script qui affiche Hello world, et logue qu'il a bien été lancé.

<?php
/*
 * Script d'exemple, affichant Hello world.
 */

//-----------------------------------------------------------------------------------------------------\\
//-------------------------------------------  INITIALISATION  ----------------------------------------\\
//-----------------------------------------------------------------------------------------------------\\

// Inclusion de la classe eZUser
require_once( 'kernel/classes/datatypes/ezuser/ezuser.php' );

// Répertoire des logs
$logFolder = 'var/monsite/log/cronjobs/';
// Fichier de logs
$logFile = 'sample.log';

// Connexion d'un utilisateur spécifique ayant les droits
// nécessaires pour effectuer le traitement
$cronjobUser = ezUser::loginUser( 'user_login', 'user_password' );

//-----------------------------------------------------------------------------------------------------\\
//---------------------------------------------  TRAITEMENTS  -----------------------------------------\\
//-----------------------------------------------------------------------------------------------------\\

// Log du début de l'exécution du script
eZLog::write( 'Démarrage du script d\'exemple', $logFile, $logFolder );

if ( is_object( $cronjobUser ) ) {

    $message = 'Hello world !';

    // Autres traitements...

    // Affichage du message sur la sortie standard (invite de commande)
    $cli->output( $message );

} else {

    $message = 'ERROR : L\'utilisateur "cronjobUser" n\'a pas pu être identifié !';

    // Stockage de l'erreur dans les logs
    eZLog::write( $message, $logFile, $logFolder );

    // Affichage du message d'erreur sur la sortie standard
    $cli->output( $message );
}

eZLog::write( 'Fin du script d\'exemple', $logFile, $logFolder );

?>

Explications :

  • On connecte un utilisateur spécifique aux cronjobs, disposant des droits nécessaires pour effectuer les opérations souhaitées (dans cet exemple ça n'a pas d'intérêt puisqu'on ne fait pas d'autres opérations en base).
  • On logue le début et la fin de l'exécution du script, pour être sûr que le script a bien été exécuté, avec succès ou non, dans un fichier portant le même nom que le cronjob.
  • On affiche des informations sur le traitement en cours sur la sortie standard, via la variable $cli directement accessible dans le script et sa méthode output().
  • Le mot de passe et le login de l'utilisateur ainsi que le chemin vers le dossier des logs, peuvent être stockés dans un fichier de configuration, surtout s'ils communs à plusieurs scripts.

Configuration

Pour qu'eZ Publish trouve votre cronjob, vous devez le déclarer dans le fichier cronjob.ini.

Par exemple dans le fichier monextension/settings/cronjob.ini.append.php :

<?php /* #?ini charset="utf-8"?

[CronjobSettings]
# Déclare que l'extension possède des cronjobs
ExtensionDirectories[]=monextension

# Déclare un cronjob au nom 'sample', dont le script est dans le fichier monextension/cronjobs/sample.php
[CronjobPart-sample]
Scripts[]=sample.php

*/ 
?>

N'oubliez pas de vider les caches ensuite.

Utilisation

Pour exécuter ce cronjob, utilisez la commande suivante à la racine de votre site :

php runcronjobs.php sample

Remarques :

  • EZ Publish fournit déjà des cronjobs, présents dans le répertoire cronjobs/ à la racine. Ils sont documentés ici.
  • Pour lancer un cronjobs sur un seul siteacces, précédez son nom par -s mon_siteacces dans la commande.
  • A priori, on souhaite que nos cronjobs s'exécutent à intervalle régulier comme des tâches planifiées. Il suffit donc au niveau système de paramétrer la crontab pour d'exécuter la commande.

Astuce [eZ4] Créer des webservices avec eZ Publish

Avant de commencer, vous devez savoir appeler les webservices natif d'eZ Publish.

Dans cet exemple, on considèrera que vous venez de créer et d'activer une nouvelle extension : monextension.

Cette extension aura l'arborescence suivante :

monextension
    classes
        controller
            rest_controller.php
        provider
            rest_provider.php
        view
            view_controller.php
    settings
        rest.ini.append.php

Remarque :

Une fois tous ces fichiers créés et remplis, vous devrez vider les caches et régénérer les autoloads.

Configuration

Vous aller devoir déclarer un nouveau provider de webservices (ou plusieurs).

Cette déclaration se fait via le fichier rest.ini, qui permet également de choisir le type d'authentification à utiliser.

Exemple de fichier rest.ini.append.php :

<?php /* #?ini charset="utf-8"?

[Authentication]
RequireAuthentication=enabled
AuthenticationStyle=ezpRestBasicAuthStyle

[ApiProvider]
ProviderClass[monprovider]=ezxRestApiProvider

*/
?>

Explications :

  • RequireAuthentication=enabled permet d'activer l'authentification pour l'accès aux webservices
  • AuthenticationStyle=ezpRestBasicAuthStyle active l'authentification basique d'eZ Publish (via les rôles)
  • [ApiProvider] est la section pour gérer les providers
  • ProviderClass[monprovider] définit un nouveau provider. C'est la base qui sera utilisée lors de l'appel des webservices qui fournira (ex : http://mon_site/api/monprovider/v1/foo).
  • ezxRestApiProvider est le nom de la classe PHP qui contiendra le provider

Provider

La classe ezxRestApiProvider va lister toutes les routes valides gérées par le provider.

Exemple de fichier rest_provider.php :

<?php

class ezxRestApiProvider implements ezpRestProviderInterface {

    /**
     * Retourne les routes versionnées au provider
     *
     * @return array
     */
    public function getRoutes() {

        $routes = array( 
            new ezpRestVersionedRoute( 
                new ezcMvcRailsRoute( '/foo', 'ezxRestController', 'foo' ), 
                1 
            ),
            new ezpRestVersionedRoute( 
                new ezcMvcRailsRoute( '/foo', 'ezxRestController', 'fooBar' ), 
                2 
            )
        );

        return $routes;
    }

    /**
     * Returns associated with provider view controller
     *
     * @return ezpRestViewController
     */
    public function getViewController() {
        return new ezxRestApiViewController();
    }

}
?>

Explications :

La route new ezpRestVersionedRoute( new ezcMvcRailsRoute( '/foo', 'ezxRestController', 'fooBar' ), 2 ) se lit comme ceci :

  • new ezpRestVersionedRoute() : on déclare une nouvelle route, qui aura pour version 2. À chaque version, on attribue un service à utiliser.
  • /foo : nom du service à appeler dans l'url (/api/monprovider/foo/2), auquel on ajoutera la version.
  • ezxRestController, nom de la classe contenant la méthode à exécuter.
  • fooBar, nom de la méthode à utiliser. Cette méthode sera recherchée dans la classe ezxRestController et avec le nom doFooBar(). (On préfixe donc le nom de la méthode par do et on y on ajoute une majuscule.)

Pour appeler la première version du service et exécuter la fonction doFoo(), on appelle donc /api/monprovider/foo/1, pour la seconde /api/monprovider/foo/2.

La méthode getViewController() détermine le contrôleur de vue à utiliser. C'est lui qui transformera les données pour être utilisables dans une requête HTTP.

Contrôleur

Le contrôleur est la classe qui contient les différentes méthodes préparant les données à retourner via les webservices.

Chaque méthode doit retourner un objet ezcMvcResult. On y stocke tout ce que l'on veut dans le champ variables.

Exemple de fichier rest_controller.php :

<?php
class ezxRestController extends ezcMvcController {

    public function doFoo() {
        $res = new ezcMvcResult();
        $res->variables['message'] = 'This is FOO !';
        $res->variables['var'] = array( 'Hello world' );
        return $res;
    }

    public function doFooBar() {
        $res = new ezcMvcResult();
        $res->variables['message'] = 'This is FOOBAR !';
        return $res;
    }
}
?>

Contrôleur de vue

Pour préparer la réponse HTTP retournée par les webservices, et notamment le format de sortie, on a besoin d'un contrôleur de vue.

Pour cet exemple, les données seront transformées sous forme de flux JSON.

Exemple de fichier view_controller.php :

<?php
class ezxRestApiViewController implements ezpRestViewControllerInterface {

    /**
    * Crée le flux de sortie retourné au contrôleur
    *
    * @param ezcMvcRoutingInformation $routeInfo
    * @param ezcMvcRequest $request
    * @param ezcMvcResult $result
    * @return ezcMvcView
    */
    public function loadView( ezcMvcRoutingInformation $routeInfo, ezcMvcRequest $request, ezcMvcResult $result ) {

        return new ezpRestJsonView( $request, $result );
    }
}
?>

Récapitulatif

Lorsqu'on appelle un webservice, une méthode est exécutée dans un premier contrôleur. Elle retourne un objet contenant des données. Un contrôleur de vue est ensuite utilisée pour formater les données et préparer la réponse HTTP.

Astuce [eZ4] Utiliser les webservices eZ Publish

EZ Publish fournit une l'extension ezprestapiprovider, désactivée par défaut. Elle expose des webservices basiques permettant par exemple de récupérer les informations d'un nœud.

Activation

Activez l'extension ezprestapiprovider dans votre site.ini.

Dans settings/override/site.ini.append.php, ajoutez une ligne :

<?php /* #?ini charset="utf-8"?

[ExtensionSettings]
ActiveExtensions[]=ezprestapiprovider

*/
?>

Vous devez avoir activé le .htaccess pour votre site (renommez le fichier .htaccess_root en .htaccess), qui contient une règle de réécriture indispensable :

DirectoryIndex index.php

RewriteEngine On
RewriteRule ^/api/ /index_rest\.php [L]
RewriteRule ^api/ index_rest.php [L]
RewriteRule ^index_rest\.php - [L]

Cette règle permet d'appeler les webservices en utilisant /api/ au lieu de /index_rest.php/.

Authentification

Pour ajouter une authentification, modifier le fichier rest.ini.

Dans settings/override/rest.ini.append.php, ajoutez une ligne :

<?php /* #?ini charset="utf-8"?

[Authentication]
RequireAuthentication=enabled
AuthenticationStyle=ezpRestBasicAuthStyle

*/
?>

Ces lignes définissent que pour accéder aux webservices, une authentification est requise, de type Basic Auth (= via les rôles d'eZ Publish).

Vous pouvez également :

  • désactiver l'authentification (RequireAuthentication=disabled)
  • utiliser oAuth (AuthenticationStyle=ezpRestOauthAuthenticationStyle).

Utilisation

Une fois ces configurations terminées et les caches vidés, vous pouvez accéder aux webservices via une url de type : http://mon-site.dev/api/ezp/content/node/<node_id>.

Cette url est composée de plusieurs parties :

  • http://mon-site.dev : la base de l'url
  • /api/ezp/ : le nom du préfixe (api) et le nom du provider (ezp), tous deux définis dans rest.ini.
  • content/node/ : le nom du service appelé
  • <node_id> : le ou les paramètres à transmettre

Le webservice appelé dans cet exemple est documenté ici. On peut ainsi connaitre les paramètres attendus et le format de retour.

Pour aller plus loin : Créer des webservices avec eZ Publish

Astuce [eZ4] Réinitialiser le mot de passe admin

Si vous ne vous souvenez plus du mot de passe de l'administrateur, ou si on vous donne un site existant sans le mot de passe admin, vous pouvez vous en sortir en consultant directement la base de données.

Utilisez phpmyadmin ou un autre interface, pour consulter la base de données :

  • dans la table ezuser, trouver la ligne contenant l'utilisateur admin.
  • modifiez la valeur du champ password_hash, pour qu'elle devienne bab77ccf06f0b1f982e11c60f344c3c2.

L'utilisateur a maintenant pour mot de passe : admin.

Remarques :

  • Cette technique fonctionne uniquement si le type de hash est 2 (password_hash_type).
  • La requête sql à exécuter pour changer le mot de passe est la suivante (ar défaut l'admin a l'ID 14) :
UPDATE ezuser SET password_hash="bab77ccf06f0b1f982e11c60f344c3c2" WHERE contentobject_id="14"

Astuce [eZ4] Ajouter un favicon

Par défaut, le favicon utilisé est design/standard/images/favicon.ico.

Vous pouvez le surcharger comme un template, en créant le fichier mon_extension/design/mon_design/images/favicon.ico, et en ajoutant la règle de surcharge dans override.ini :

<?php /* #?ini charset="utf-8"?

[favicon]
Source=images/favicon.ico
MatchFile=images/favicon.ico
Subdir=templates

*/ ?>

Astuce [eZ4] Indexer le contenu des fichiers

Pour que le contenu des fichiers soit indexé et ainsi recherchable, il faut utiliser l'extension eztika.

Elle convertit les documents .pdf, .xls ou autres, en fichiers texte, qu'elle peut ensuite parser et indexer. Elle a besoin pour cela d'exécuter le fichier extension/eztika/bin/eztika.

  • Installez et activez l'extension eztika
  • Vérifier que le fichier extension/eztika/bin/eztika est bien exécutable
  • Régénérez les autoloads :
php bin/php/ezpgenerateautoloads.php -e

En cas de problème, consultez le fichier INSTALL.TXT présent dans le répertoire de l'extension.

Astuce [eZ4] Modifier les paramètres d'envoi de mail

EZ Publish propose un système d'envoi de mails, par collecte d'informations. Le template du mail envoyé est extension/mon_extension/design/mon_design/templates/content/collectedinfomail/my_form.tpl, avec my_form le nom de la classe de contenu gérant le formulaire d'envoi de mail.

Les paramètres du mail doivent être définis directement dans ce template, par définition de blocs.

Pour modifier le sujet du mail, utilisez par exemple:

{set-block scope=root variable=subject}Mon sujet de mail{/set-block}

La liste des paramètres acceptés (ici subject) sont listés dans le fichier kernel/content/collectinformation.php à partir de la ligne 272 :

  • subject : sujet de l'email
  • email_receiver : email du destinataire
  • email_cc_receivers : destinataires en copie
  • email_bcc_receivers : destinataires en copie cachée
  • email_sender : email de l'expéditeur
  • email_reply_to : email de réponse
  • redirect_to_node_id : node_id du nœud vers lequel effectuer une redirection après l'envoi de l'email

Astuce [eZ4] Afficher un pager

EZ Publish possède un pager prêt à l'emploi (ou presque) : extension/ezwebin/design/ezwebin/templates/content/search.tpl.

Pour l'utiliser, appelez-le dans votre template affichant une liste d'éléments :

{* Nombre maximum d'éléments par page *}
{def $page_limit = 10}

{* Nombre total d'éléments *}
{def $nb_total_articles = fetch( 
    'content', 'list_count',
    hash( 
        'parent_node_id', $node.node_id,
        'class_filter_type', 'include',
        'class_filter_array', array( 'article' ) 
    ) 
)}

{* Liste des éléments depuis $view_parameters.offset jusqu'à $page_limit *}
{def $articles = fetch( 
    'content', 'list',
    hash( 
        'parent_node_id', $node.node_id,
        'offset', $view_parameters.offset,
        'limit', $page_limit,
        'class_filter_type', 'include',
        'class_filter_array', array( 'article' ) 
    ) 
)}

{* Affichage des noms des éléments *}
{foreach $articles as $article }
    {$article.name|wash()}
{/foreach}

{* Affichage du pager *}
{include name=navigator
         uri='design:navigator/google.tpl'
         page_uri=$node.url_alias
         item_count=$nb_total_articles
         view_parameters=$view_parameters
         item_limit=$page_limit}

Remarques :

  • page_uri contient l'url de la page dans laquelle vous afficher le pager. Si vous êtes dans la vue d'un de vos modules, page_uri ressemblera plutôt à mon_module/ma_vue.
  • item_limit définit le nombre maximum d'éléments à afficher. Ce paramètre doit être passé en paramètre pour le fetch qui récupère la liste des éléments. On peut rendre ce nombre paramétrable via un fichier .ini (ex: mon_extension.ini).
  • Le template du pager peut être surchargé comme n'importe quel autre template.

Le template du pager a besoin des paramètres de la vue ($view_parameters). Si vous êtes dans la vue d'un de vos modules, vous devez gérer ce paramètre vous même et le transmettre à votre template.

Pour cela, ajoutez un paramètre non ordonné à votre vue, dans le fichier module.php :

$ViewList['ma_vue'] = array(
    'functions' => array( 'ma_vue' ),
    'script' => 'maVue.php',
    'unordered_params' => array( 'offset' => 'Offset' )
);

Dans votre vue récupérez l'offset et passez-le à votre template :

$viewParameters = array( 'offset' => 0 );
if ( isset( $Params['Offset'] ) ) {
    $viewParameters['offset'] = $Params['Offset'];
}
$tpl->setVariable( 'view_parameters', $viewParameters );

Astuce [eZ4] Créer un formulaire d'envoi de mail

Voici une méthode pour créer un formulaire d'envoi de mail en utilisant au maximum l'API d'eZ Publish.

  • Commencez par créer une nouvelle classe de contenu.
  • Ajoutez des champs sujet et message de type collecteur d'informations.
  • Déclarez maintenant le formulaire dans collect.ini.

Par exemple dans extension/mon_extension/settings/collect.ini.append.php :

<?php /* #?ini charset="utf-8"?

[InfoSettings]
# Associe l'identifier de la classe de contenu au type de collection d'information
TypeList[my_form]=my_form
TypeAttribute=collection_type

[EmailSettings]
# Définit que les informations collectées doivent être envoyées par email
SendEmailList[my_form]=enabled
SendEmailAttribute=collection_email

[DisplaySettings]
# Définit l'action a effectué une fois que les informations sont collectées (ici on affiche le résultat = message informant que l'email a bien été envoyé)
DisplayList[]
DisplayList[my_form]=result

[CollectionSettings]
# Autorise les utilisateurs anonymes à utiliser le formulaire
CollectAnonymousDataList[]
CollectAnonymousDataList[my_form]=enabled

# Autorise un utilisateur à utiliser plusieurs fois le formulaire
CollectionUserData=multiple
CollectionUserDataList[my_form]=multiple

*/ ?>

Créez les templates nécessaires :

  • extension/mon_extension/design/mon_design/override/templates/full/my_form.tpl
  • extension/mon_extension/design/mon_design/templates/content/collectedinfo/my_form.tpl
  • extension/mon_extension/design/mon_design/templates/content/collectedinfomail/my_form.tpl

Le premier contient le formulaire d'envoi de mail
Le deuxième contient la page de résultat à afficher une fois que le mail a été envoyé
Le troisième contient le contenu du mail et ses paramètres (destinataire, sujet, ...)

Remarques :

Astuce [eZ4] Echapper un bloc XML pour l'utiliser en javascript

Problème

Imaginons que vous vouliez stocker le contenu d'un champ de type bloc XML dans une variable javascript.

D'habitude, on affiche le champ de cette manière dans le template :

{attribute_view_gui attribute=$node.data_map.description}

D'autre part, si on veut échapper une chaîne pour pouvoir sans risque l'utliiser en javascript, on utiliser la fonction de template wash().

Pas facile à première vue de concilier les deux.

Solution

Stockez le rendu de votre bloc XML dans une variable :

{set-block variable=$description}
    {attribute_view_gui attribute=$node.data_map.description}
{/set-block}

Échappez la variable pour pouvoir utiliser son contenu dans du code javascript :

<script>
jsVar = "{$description|trim()|wash('javascript')}"; 
</script>

Remarque :

Si la variable contient des retours à la ligne, cela causera des erreurs. Pour cela on peut créer un opérateur de template qui recherchera et supprimera tous ces retours à la ligne.

/**
 * Supprime tous les retours à la ligne.
 * Cette fonction est utile pour injecter du code html généré par un template dans du javascript. (exemple : dans une infobulle Google Map)
 * @param string $string Chaîne à nettoyer
 */
public static function removeWraps( $string ) {
    $string = str_replace( '\r\n', '', $string );
    $string = str_replace( '\n', '', $string );
    $string = str_replace( '\r', '', $string );
    $string = str_replace( CHR( 10 ), '',$string );
    $string = str_replace( CHR( 13 ), '', $string );
    return $string;
}

Astuce [eZ4] Obtenir un noeud courant dans la vue d'un module

  • Déclarez le paramètre NodeID comme paramètre ordonné de la vue.
  • Récupérez le nœud, côté PHP, dans la vue de votre module :
$nodeID = intval( $Params['NodeID'] );
$node   = eZContentObjectTreeNode::fetch( $nodeID );
  • Déclarez la variable $node pour le template :
$tpl->setVariable( 'node', $node );
  • Renvoyer le node_id dans le résultat du module :
$Result['node_id'] = $myNodeId;
return $Result;

Astuce [eZ4] Gérer efficacement le cache

  • Pour qu'un template ne soit pas mis en cache, utilisez :
{set-block scope=global variable=cache_ttl}0{/set-block}
  • Pour qu'une partie de ce template soit mise en cache, utilisez :
{cache-block}
    <p>Ce texte sera mis en cache</p>
{/cache-block}
  • Pour qu'un cache soit mis à jour si un nœud fils (ou plus profond) est modifié, utilisez :
{cache-block subtree_expiry=$my_node.node_id}
    <p>Ce texte sera conservé en cache jusqu'à ce qu'un fils de $my_node soit modifié.</p>
{/cache-block}

Pour qu'un cache soit créé pour chaque valeur différente de certains paramètres, utilisez :

{cache-block keys=array( $param1, $param2 )}
    <p>
    Ce texte sera conservé en cache sous plusieurs versions, 
    une pour chaque couple {$param1} et {$param2} différent.
    </p>
{/cache-block}

Astuce [eZ4] Utiliser un template présent dans override/ comme template de vue

<?php /* #?ini charset="utf-8"?

[mavue_mon_type_de_noeud]
Source=monmodule/mavue.tpl
MatchFile=monmodule/mavue/mon_type_de_noeud.tpl
Subdir=templates
Match[class_identifier]=mon_type_de_noeud

*/ ?>
  • Déclarez cette surcharge pour le template dans le fichier .php de la vue du module :
$res = eZTemplateDesignResource::instance();
$designKeys = array( array( 'class_identifier', $node->attribute( 'class_identifier' ) ),
                     array( 'parent_node_id', $node->attribute( 'parent_node_id' ) ) );
$res->setKeys( $designKeys );

Astuce [eZ4] Passer des paramètres à la vue content/view

  • Appelez le module sans utiliser l'url_alias et ajouter les paramètres entre parenthèses. Par exemple /content/view/full/2/(mon_param)/sa_valeur
  • Les valeurs passées sont récupérables via :
{$view_parameters.ma_var} 

Astuce [eZ4] Récupérer le noeud racine

Pour récupérer le nœud racine dans un template, au moins deux méthodes sont possibles :

{def $root_node = fetch( 'content', 'node', hash( 'node_path', '/') )}

et

{def $root_node = fetch( 'content', 'node', hash( 'node_id', ezini( 'NodeSettings', 'RootNode', 'content.ini' ) )}

Remarque :

La deuxième solution retourne le nœud racine pour le siteaccess courant alors que la première retourne toujours le nœud root (dont le node_id est 2).

Astuce [eZ4] Récupérer les noeuds d'une relation d'objets

Lorsqu'un objet a un champ de type relation d'objets, il peut retourner un ou plusieurs objets liés.

Dans un template

Pour récupérer les nœuds principaux des objets liés, utilisez dans un template :

{def $related_nodes = array()}
{if $node.data_map.attribut_relation_objets.has_content}

    {def $related_objects = $node.data_map.relationlist_attribute.content.relation_list}

    {def $node_id_list = array()}
    {foreach $related_objects as $object_item}
        {set $node_id_list = $liste_node_id|append( $object_item.node_id )}
    {/foreach}
    {set $related_nodes = fetch( 'content', 'node', hash( 'node_id', $node_id_list ) )}

    {if is_object( $related_nodes )}
        {set $related_nodes = array( $related_nodes )}
    {/if}

    {undef $node_id_list}
    {undef $related_objects}
{/if}

Explications :

  • On vérifie que l'attribut relationlist_attribute de type relation d'objets a un contenu.
  • Si oui, on en parcourt les éléments et on stocke le node_id de chacun d'eux
  • On récupère tous les nœuds correspondant à la liste des node_id avec le fetch().
  • S'il n'y a qu'un résultat, le fetch retourne un objet. On le met donc dans un tableau.

Remarque :

Si on sait qu'on n'aura jamais plus d'un objet lié, on peut faire simplement :

{def $related_node = false()}
{if $node.data_map.attribut_relation_objets.has_content}
    {set $related_node = fetch( 'content', 'node', hash( 'node_id', $node.data_map.attribut_relation_objets.content.relation_list.0.node_id ) )}
{/if}

Côté PHP

$relatedNodeList = array();

$dataMap = $node->dataMap();
$relationListAttribute = $dataMap['relationlist_attribute'];

if ( $relationListAttribute->hasContent() ) {

    $relatedObjects = $relationListAttribute->content();
    $nodeIdList = array();

    foreach ( $relatedObjects['relation_list'] as $object_item ) {
        $nodeIdList[] = $object_item['node_id'];
    }

    $relatedNodeList = parent::fetch( $nodeIdList );
}

Explications :

Le fonctionnement est similaire à celui dans le template.
De la même manière, on peut utiliser une version simplifiée quand la relation est unique :

$relatedNode = false;

$dataMap = $node->dataMap();
$relationListAttribute = $dataMap['temp'];

if ( $relationListAttribute->hasContent() ) {
    $relatedObjects = $relationListAttribute->content();
    $relatedNode = parent::fetch( $relatedObjects['relation_list'][0]['node_id'] );
}

Astuce [eZ4] Surcharger une classe du kernel

Pour corriger un bug ou étendre les fonctionnalités natives d'eZ Publish, on peut être tenter de modifier les fichiers du cœur du CMS, dans le dossier kernel/.

Il faut toujours éviter de modifier ces fichiers, surtout qu'il est possible de le faire proprement dans une extension, grâce au système d'autoload.

Pré-requi

Vous devez d'abord autoriser les surcharges des classes du kernel, dans le fichier config.php, à la racine du site. (Si ce fichier n'existe pas, copiez le fichier config.php-RECOMMENDED et renommez-le en config.php.)

Il faut modifier la constante EZP_AUTOLOAD_ALLOW_KERNEL_OVERRIDE pour la rendre égale à true :

define( 'EZP_AUTOLOAD_ALLOW_KERNEL_OVERRIDE', true );

Surcharge

Vous pouvez maintenant copiez dans votre extension le fichier du kernel que vous souhaitez modifier.

Par exemple extension/mon_extension/classes/override/ezcontentobjecttreenode.php.

Il faut ensuite régénérer les autoloads et vider le cache, via les commande suivantes exécutées à la racine du site :

php bin/php/ezpgenerateautoloads.php -o
php bin/php/ezpgenerateautoloads.php
php bin/php/ezcache.php --clear-all

Votre classe surcharge maintenant celle du kernel, et est référencée dans le fichier var/autoload/ezp_override.php :

<?php
/**
 * Autoloader definition for eZ Publish Kernel overrides files.
 *
 * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
 * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
 * @version  2012.2
 * @package kernel
 *
 */

return array(
    'eZContentObjectTreeNode' => 'extension/apitools/classes/override/ezcontentobjecttreenode.php',
);

?>

Astuce [eZ4] Diminuer le taux de compression des images

Si les images du site sont trop lourdes, ou de mauvaise qualité, vous pouvez modifier le taux de compression utilisé lors de la génération des thumbnails.

Pour cela, créez ou modifiez le fichier image.ini.append.php dans votre extension.

Exemple, pour le fichier extension/mon_extension/image.ini.append.php :

<?php /* #?ini charset="utf-8"?

[MIMETypeSettings] :
Quality[]=image/jpeg;100
Quality[]=image/png;100
Quality[]=image/gif;100

*/ ?>

Pour chaque format d'image, vous pouvez définir le taux de compression par rapport à l'image originale. 100% correspond à la qualité maximale utilisable.

Astuce [eZ4] Internationaliser un texte

Créez un fichier de traduction dans votre extension :

extension/mon_extension/translations/ma_lang/translation.ts avec ma_lang l'identifiant d'un langage (ex: fre-FR).

Malgré l'extension ts, il est formaté en XML :

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0">

<!-- Contexte -->
<context>
    <name>mon/contexte</name>
    <message>
        <source>Ma phrase à traduire</source>
        <translation>Ma phrase traduite</translation>
    </message>
</context>

</TS>

Pour traduire un mot ou une phrase dans un template, utilisez la méthode i18n() :

{* La ligne suivante affichera 'Ma phrase traduite' *}
{'Ma phrase à traduire'|i18n( 'mon/contexte' )}

Remarque :

Les contextes permettent de regrouper les traductions, par thème, par page, etc.

Astuce [eZ4] Redimensionner automatiquement les images

Pour éviter que l'image uploadée par le contributeur ne dégrade l'affichage, si elle est trop grande par exemple, vous pouvez la redimensionner automatiquement. Le redimensionnement consiste à créer un thumbnail de l'image, qui sera utilisé à sa place à l'affichage.

Prérequis

Pour générer les thumbnails, un convertisseur d'images doit être installé sur la machine. (ex: ImageMagick) Pour l'activer, créez ou modifiez le fichier /settings/override/image.ini.append.php :

<?php /* #?ini charset="utf-8"?

[ImageMagick]
IsEnabled=true
# Chemin vers l'exécutable
ExecutablePath=C:/Program Files/ImageMagick-6.7.3-Q16
# Nom de l'exécutable
Executable=convert.exe

*/ ?>

Configuration

Créez ou modifiez le fichier extension/mon_extension/settings/siteaccess/mon_siteaccess/image.ini.append.php en ajoutant :

<?php /* #?ini charset="utf-8"?

[AliasSettings]
AliasList[]=mon_nom_de_definition

[mon_nom_de_definition]
Reference=
Filters[]
Filters[]=geometry/scaledownonly=86;47

*/ ?>

Explication :

86 et 47 sont la largeur et la hauteur maximalesde l'image en pixels. Lors de l'affichage, si l'une de ces deux mesures est trop importante, l'image est redimensionnée proportionnellement (= un thumbnail est généré et il remplacera l'image à l'affichage).

Remarque :

On peut également ne limiter que la largeur ou la hauteur, en utilisant scalewidth et scaleheight au lieu de scaledownonly.

Utilisation

Pour que l'image soit redimensionnée, dans le template appelez :

{attribute_view_gui attribute=$node.data_map.image image_class='mon_nom_de_definition'}

ou :

<img src="{$node.data_map.image.content[mon_nom_de_definition].full_path|ezroot( 'no' )}" alt="" />

Astuce [eZ4] Surcharger un template d'affichage

J'ai créé une nouvelle classe de contenu Article, et je souhaite modifier le template utilisé pour les articles.

Je dois d'abord commencer par déclarer mon extension comme une extension de design, dans le fichier design.ini.

Exemple de fichier extension/mon_extension/settings/siteaccess/site/design.ini.append.php :

<?php /* #?ini charset="utf-8"?

[ExtensionSettings]
DesignExtensions[]=mon_extension

*/ ?>

Ensuite, il faut déclarer les règles de surcharge que l'on souhaite appliquer, dans le fichier override.ini.

Exemple de fichier extension/mon_extension/settings/siteaccess/site/override.ini.append.php :

<?php /* #?ini charset="utf-8"?

[full_article]
Source=node/view/full.tpl
MatchFile=node/view/full/article.tpl
Subdir=templates
Match[class_identifier]=article

*/ ?>
  • On définit une section full_article, pour surcharger l'affichage pleine page (full) des articles.
  • La source définit le template à surcharger. Dans le cas de l'affichage full, c'est node/view/full.tpl qui est surchargé.
  • Matchfile définit le template qui remplacera celui par défaut.
  • Subdir définit dans quel sous-dossier de extension/mon_extension/design/mon_design/override/ le matchfile sera recherché.
  • Le Match[class_identifier] définit pour quelle(s) classe(s) de contenu la règle de surcharge doit s'appliquer.

Dans mon extension, j'ai donc :

mon_extension
    | settings
          | siteaccess
                | site
                      | design.ini.append.php
                      | override.ini.append.php
    | override
          | templates
                | node
                      | view
                           | full
                                 | article.tpl

Remarque :

La liste des Match disponibles en fonction du template à surcharger est visible dans la documentation officielle.

Astuce [eZ4] Surcharger les icônes du site

EZ Publish permet de surcharger les différents icônes du site et en particulier ceux du back-office. Le fichier de paramétrage de ces icônes est icon.ini.

Le système de gestion des icônes n'est malheureusement pas très bien géré, et il est complexe de l'utiliser de manière "pluginisée".

Voici deux méthodes pour surcharger les icônes :

  • La première, plus simple, oblige à ajouter des fichiers dans un répertoire extérieur à une extension.
  • La seconde, plus propre, oblige à copier tous les icônes par défaut dans son extension.

Méthode simple

Créez le fichier icon.ini.append.php dans votre extension.

Exemple de fichier extension/mon_extension/settings/siteaccess/bo/icon.ini.append.php :

<?php /* #?ini charset="utf-8"?

[ClassIcons]
# Mapping entre un class_identifier et l'icône à utiliser
# Le chemin complet depuis les dossiers 16x16 et 32x32 doit être précisé

# Nouveaux icônes pour les classes créées
ClassMap[category]=custom/category.png
ClassMap[article]=custom/article.png

*/ ?>

Remarques :

Pour fonctionner, vous devez également créer les fichiers category.png et article.png, dans :

  • share/icons/crystal-admin/16x16_ghost/custom
  • share/icons/crystal-admin/16x16_indexed/custom
  • share/icons/crystal-admin/16x16_original/custom
  • share/icons/crystal-admin/32x32/custom

Pour chaque nouvelle classe de contenu avec un icône spécifique, ajoutez une entrée dans le fichier icon.ini.append.php de votre extension, et les images dans les 4 dossiers ci-dessus.

Méthode propre

Commencez par copier le contenu du fichier share/icons/crystal-admin/icon.ini dans un fichier icon.ini.append.php, dans votre extension.

Exemple de fichier extension/mon_extension/settings/siteaccess/bo/icon.ini.append.php :

<?php /* #?ini charset="utf-8"?

[IconSettings]
# Dossier dans lequel les thèmes des icônes seront recherchés
Repository=extension/mon_extension/share/icons

# Thème d'icônes à utiliser (= nom du dossier à créer dans extension/mon_extension/share/icons)
Theme=mon_theme

# Noms des dossiers d'icônes à utiliser en fonction de la situation (= normal/small/...)
# Ces dossiers devront être présents dans le dossier du thème (ici dans mon_theme)
# Contrairement au fichier icon.ini par défaut, on utilise le même répertoire pour les affichages "small", "ghost" et "original"
Size=normal
Sizes[normal]=32x32
Sizes[small]=16x16
Sizes[ghost]=16x16
Sizes[original]=16x16

[MimeIcons]
# Icône par défaut quand aucun mimetype ne correspond au fichier
Default=mimetypes/binary.png

# Mapping entre les mimetypes et les icônes à utiliser. 
# Le chemin complet depuis les dossiers 16x16 et 32x32 doit être précisé
MimeMap[]
MimeMap[text]=mimetypes/ascii.png
MimeMap[image]=mimetypes/image.png
MimeMap[video]=mimetypes/video.png
MimeMap[audio]=mimetypes/sound.png
MimeMap[application/x-gzip]=mimetypes/tgz.png
MimeMap[application/x-bzip2]=mimetypes/tgz.png
MimeMap[application/x-tar]=mimetypes/tgz.png
MimeMap[application/zip]=mimetypes/tgz.png
MimeMap[application/x-rpm]=mimetypes/rpm.png
MimeMap[application/vnd.ms-powerpoint]=mimetypes/powerpoint.png
MimeMap[application/msword]=mimetypes/word.png
MimeMap[application/vnd.ms-excel]=mimetypes/excel.png
MimeMap[application/pdf]=mimetypes/pdf.png
MimeMap[application/postscript]=mimetypes/pdf.png
MimeMap[text/html]=mimetypes/html.png
MimeMap[video/quicktime]=mimetypes/quicktime.png
MimeMap[video/video/vnd.rn-realvideo]=mimetypes/real_doc.png

[ClassGroupIcons]
# Icône par défaut pour un nouveau groupe de classes de contenu
Default=filesystems/folder.png

ClassGroupMap[]
ClassGroupMap[content]=filesystems/folder_txt.png
ClassGroupMap[users]=apps/kuser.png
ClassGroupMap[media]=filesystems/folder_video.png
ClassGroupMap[setup]=apps/package_settings.png

[Icons]
# Icône par défaut pour un élément divers
Default=mimetypes/empty.png

IconMap[]
IconMap[role]=actions/identity.png
IconMap[section]=actions/view_tree.png
IconMap[translation]=apps/locale.png
IconMap[pdfexport]=apps/acroread.png
IconMap[url]=apps/package_network.png

[ClassIcons]
# Icône par défaut pour une nouvelle classe de contenu
Default=mimetypes/empty.png

# Mapping entre un class_identifier et l'icône à utiliser
# Le chemin complet depuis les dossiers 16x16 et 32x32 doit être précisé
ClassMap[]

ClassMap[comment]=mimetypes/txt2.png
ClassMap[common_ini_settings]=apps/package_settings.png
ClassMap[company]=apps/kuser.png
ClassMap[file]=mimetypes/binary.png
ClassMap[folder]=filesystems/folder.png
ClassMap[forum]=filesystems/folder_man.png
ClassMap[forum_message]=mimetypes/txt2.png
ClassMap[forum_reply]=mimetypes/txt2.png
ClassMap[forum_topic]=mimetypes/txt2.png
ClassMap[gallery]=filesystems/folder_image.png
ClassMap[image]=mimetypes/image.png
ClassMap[link]=mimetypes/html.png
ClassMap[person]=apps/personal.png
ClassMap[poll]=mimetypes/log.png
ClassMap[product]=apps/package.png
ClassMap[product_review]=mimetypes/txt2.png
ClassMap[multiprice_product]=apps/package.png
ClassMap[dynamic_vat_product]=apps/package.png
ClassMap[quicktime]=mimetypes/quicktime.png
ClassMap[real_video]=mimetypes/real_doc.png
ClassMap[review]=mimetypes/txt2.png
ClassMap[template_look]=apps/package_settings.png
ClassMap[user]=apps/personal.png
ClassMap[user_group]=apps/kuser.png
ClassMap[weblog]=mimetypes/document.png
ClassMap[windows_media]=mimetypes/video.png
ClassMap[user]=apps/personal.png
ClassMap[user_group]=apps/kuser.png

# Nouveaux icônes pour les classes créées
ClassMap[frontpage]=custom/home.png
ClassMap[category]=custom/category.png
ClassMap[article]=custom/article.png

*/ ?>

Il faut ensuite copier le contenu du dossier share/icons/crystal-admin/16x16_original dans extension/mon_extension/share/icons/mon_theme/16x16 et le contenu du dossier share/icons/crystal-admin/32x32 dans extension/mon_extension/share/icons/mon_theme/32x32, pour conserver les icônes déjà existants.

Les nouveaux icônes sont à ajouter dans les dossiers .../mon_theme/16x16/custom et .../mon_theme/32x32/custom.

Remarques :

  • Dans cet exemple, le nombre d'icônes est réduit, puisqu'on utilise le même fichier pour les affichages small, ghost et indexed.
  • Si vous avez activé le .htaccess pour votre site, vous devez y ajouter une règle de réécriture, sans quoi vos icônes ne seront pas acessibles :
RewriteRule ^extension/[^/]+/share/icons/[^/]+/.* - [L]

Astuce [eZ4] Définir les fichiers css et javascript à inclure pour la page courante

Il y a 3 moyens d'inclure des fichiers CSS et Javascript dans une page eZ Publish.

Directement dans le header

Comme toute page html, on peut définir des fichiers CSS et Javascript dans la balise <header>. À priori vous pourrez le modifier dans votre fichier pagelayout.tpl.

Par exemple, dans le fichier monextension/design/mon_design/override/templates/pagelayout.tpl :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{$site.http_equiv.Content-language|wash}" lang="{$site.http_equiv.Content-language|wash}">
<head>

{def $pagedata         = ezpagedata()
     $pagestyle        = $pagedata.css_classes
     $locales          = fetch( 'content', 'translation_list' )
     $pagedesign       = $pagedata.template_look
     $current_node_id  = $pagedata.node_id
     $current_node     = fetch( 'content', 'node', hash( 'node_id', $pagedata.node_id ) )}

<link rel="stylesheet" type="text/css" href="/extension/monextension/design/mon_design/stylesheets/mon_design.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.3.js" charset="utf-8"></script>

{include uri='design:page_head.tpl'}
{include uri='design:page_head_style.tpl'}
{include uri='design:page_head_script.tpl'}
</head>

Explication :

Une feuille CSS présente dans monextension/ et un script JS ont été inclus à la page.

Remarque :

De la même manière, le fichier JS peut être inclus à la fin du <body> à la place, ce qui est préférable d'un point de vue optimisation.

Dans les propriétés du fichier design.ini

Vous pouvez définir des fichiers CSS et Javascript à inclure sur toutes vos pages, en front-office ou en back-office, dans le fichier design.ini.

Par exemple, dans le fichier monextension/settings/design.ini.append.php :

<?php /* #?ini charset="utf-8"?

[StylesheetSettings]
# CSS Pour le front office
FrontendCSSFileList[]=structure.css
FrontendCSSFileList[]=global.css
FrontendCSSFileList[]=override.css
FrontendCSSFileList[]=wysiwyg.css
FrontendCSSFileList[]=noscript.css

[JavaScriptSettings]
JavaScriptList[]=https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js

Ces fichiers seront automatiquement ajoutés via les templates page_head_style.tpl et page_head_script.tpl à inclure dans votre balise <header>.

Par exemple, dans le fichier monextension/design/mon_design/override/templates/pagelayout.tpl :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{$site.http_equiv.Content-language|wash}" lang="{$site.http_equiv.Content-language|wash}">
<head>
{include uri='design:page_head_style.tpl'}
{include uri='design:page_head_script.tpl'}
</head>

Pour une page spécifique uniquement

Si vous avez besoin d'un fichier CSS ou Javascript particulier pour une page précise de votre site, l'extension ezjscore fournie avec eZ Publish propose des opérateurs de template bien pratiques :

{ezcss_require('override.css')}
{ezscript_require('lib/jquery-ui-1.8.16.custom.min.js')}

Remarques :

  • Si vous utilisez ezcss_require() ou ezscript_require(), les fichiers que vous passez en arguments sont mémorisés par eZ Publish.
    C'est à l'appel des opérateurs ezcss_load() et ezscript_load() qu'ils seront inclus effectivement au flux de la page. Il vous faut donc ajoutez ces derniers dans votre header/pied de page.

  • Si le mode développement est désactivé, ezcss_load() et ezscript_load() vont automatiquement minifier tous le CSS et le Javascript en deux fichiers, qui seront mis en cache.

  • Si l'une de vos feuilles CSS contient un @import pour en inclure une autre, cette dernière ne sera pas mis en cache et sera alors introuvable. Évitez-donc d'utiliser @import !