• Search:

Top menu



Planet eZ publish




ez projects

› Additional tab in admin interface

There is a new tab in eZ publish admin interface, the name of this tab os configured ib ini files, so you can call with the name of your particular CMIS server.

29/09/2009 10:17 pm (UTC)   eZ Projects   View entry   Digg!  digg it!   del.icio.us  del.icio.us

community news (ez.no)  eZ systems employee

› eZ Publish 4.2.0 released

We are proud to announce the release of eZ Publish 4.2. This release contains many new features and over 250 bug fixes in both the kernel as in its associated extensions compared to eZ Publish 4.1.0

A long list of extensions that are available to enhance your eZ Publish installation is given later in this article.

29/09/2009 4:31 pm (UTC)   Community news (ez.no)   View entry   Digg!  digg it!   del.icio.us  del.icio.us

gilles guirand

› Faire de l'édition frontale AJAX avec eZ Publish & Mootools (Partie 2)

Ce billet décrit pas à pas le développement de l'édition frontale dont le fonctionnement est visible sur la vidéo du précédent billet (principe du cliquer / éditer). Le framework JavaScript utilisé est Mootools, dont le fonctionnement est tout à fait similaire à ses principaux concurrents, à savoir : jQuery, Prototype & Script.aculo.us, YUI, Dojo, etc.

Pour ceux qui n'ont jamais utilisés un framework JavaScript, j'en profite pour introduire les objectifs et les fonctionnalités de base que l'on peut trouver dans tous ces frameworks, sans pour autant vous aider à faire un choix... Les critères de choix d'un framework dépendent de bien d'autres critères que leur simple capacités individuelles, comme par exemple :

  • Eviter la multiplication des frameworks sur un même site (ce que l'on constate malheureusement un peu partout)
  • Rester dans la logique de dépendance d'un framework et de son environnement de développement (YUI pour eZ Publish, Dojo pour Zend, Prototype pour Symfony, etc.)
  • Maîtriser un framework correctement, plutôt que 3 frameworks passablement

Introduction aux FrameWorks JavaScript

Un framework JavaScript est écrit en JavaScript, et n'est donc pas plus puissant que le langage lui même. Il s'agit seulement d'un ensemble de librairies et d'extensions du modèle objet qui facilitent l'écriture du code, notamment pour :

  • Manipuler le DOM (atteindre, ajouter, modifier ou supprimer des balises et des attributs dans la page)
  • Manipuler les évènements (évènements de la souris, du clavier)
  • Automatiser des effets complexes (effets de fondus, de Drag & Drop, etc.)
  • Faciliter les appels et traitements AJAX
  • Faciliter la POO, en proposant une syntaxe plus familière (classes, héritage)
  • Masquer certaines incompatibilités de navigateurs

La popularisation de ces frameworks a permis de démocratiser un modèle objet de manipulation du DOM, ainsi qu'une syntaxe grammaticale de sélection de noeuds déclinée du CSS3 Selector (dont voici une liste d'exemple en forme de benchmark pro-Mootools) bien plus lisible et compréhensible que l'API DOM & XPATH du W3C, ce qui génère d'ailleurs des initiatives de portage hors JavaScript comme par exemple : http://code.google.com/p/phpquery/

Spécification de la classe Mootools ajaxwebin

Cette classe a pour vocation de déclarer et d'exécuter le comportement de type "cliquer / éditer" sur un ensemble d'éléments similaires dans la page, comme par exemple :

  • Cas 1 : Remplacer sur clic toutes les balises A contenant une classe CSS 'cuvee_invert', par un champs de saisie
  • Cas 2 : Remplacer sur clic toutes les balises A, positionnées sous une balise TD et contenant une classe CSS commençant par 'stock_', par une liste de déroulante de 1 à 10

Prérequis dans le xHTML :

Les xHTML propulsés doivent être préparés pour faciliter l'identification des éléments éditables, et des correspondances entre les objets et les attributs cibles. Le motif xHTML est la suivante :

<HTMLElement id="rootID_ContentObjectID" class="MatchName" />
 

Par exemple concernant le cas 1 :

<a class="cuvee_invert" id="cuveename_2224"></a>
 

Par exemple concernant le cas 2 :

<td class="td80 stock_3" id="stock_2254"><a href="#">3</a></td>
 

Voici la liste des paramètres et leurs rôles respectifs :

  • ajaxmodule (obligatoire) : Racine du module eZ Publish : ajaxmodule/attributeID/objectID/value
  • parentMatch (obligatoire) : Syntaxe CSS3 (transmise en l'état à Mootools dans un sélecteur) qui permet d'atteindre tous les éléments impactés par l'édition frontale
  • targetMatch (optionnel) : Balise optionnelle à atteindre, comme enfant des éléments résultat du parentMatch (permet par exemple d'atteindre les balises A, enfants des balises TD)
  • styleMatch (optionnel) : Permet de définir un style CSS pour faciliter l'identification des éléments éditables dans la page (bordures, couleurs)
  • rootID (obligatoire) : <HTMLElement id="rootID_ContentObjectID" class="MatchName" />
  • attributeID (obligatoire) : Permet de transmettre le paramètre dans l'URL ajaxmodule/attributeID/objectID/value
  • caption (obligatoire) : Permet d'affecter le masque de saisie personnalisé sur clic, détaillé dans le prochain billet

Instanciation des objets de la classe Mootools ajaxwebin

A noter : les objets instanciés dans la propriété 'caption' seront décrits dans le prochain billet

window.addEvent('domready', function() {
 
    var myajaxwebin = new Array();
    
    // Ajout de la gestion des stocks
    myajaxwebin.push( new ajaxwebin({  
     ajaxmodule: '/ajaxwebin/action',
     parentMatch: 'td[class*="stock_"]',
     targetMatch: 'a',
     styleMatch: {border: '1px dotted #ff0000', background: '#ffffdd'},
     rootID: 'stock_',
     attributeID: 'stock',
     caption: (
        new ajaxcaptionsSelect({  
            'minValue': 0,
            'maxValue': 12,
            'setStyles': {margin: '0 -10px', border: '1px dotted #000', background: '#ffffdd'}
        }))
    }));
    
    // Ajout de la gestion des title des cuvées
    myajaxwebin.push( new ajaxwebin({  
     ajaxmodule: '/ajaxwebin/action',
     parentMatch: 'a[class="cuvee_invert"]',
     styleMatch: {border: '1px dotted #ff0000', background: '#ffffdd'},
     rootID: 'cuveename_',
     attributeID: 'title',
     caption: (
        new ajaxcaptionsInput({  
            'setStyles': {border: '1px dotted #000', background: '#ffffdd'}
        }))
    }));
 
});
 
 

Code commenté de la classe Mootools ajaxwebin

Il est difficile d'expliquer et de décrire l'ensemble du code. Les commentaires devraient être suffisants quant à la compréhension des mécanismes. Cependant voici quelques éléments clés de compréhension :

  • Voir la documentation des classes sur Mootools, et notamment l'utilisation du setOptions
  • Voir la documentation sur bind qui est essentielle à la construction des mécanismes qui mixent la construction dynamique du DOM et l'ajout dynamique d'évènements. Cette méthode permet de modifier la portée du this, pour définir quelle est la valeur du this dans l'exécution des évènements (clics ou autres)
var ajaxwebin = new Class({
    
    Implements: [Options],
 
    options: {
       ajaxmodule: null,
       parentMatch: null,
       targetMatch: null,
       styleMatch: null,
       rootID: null,
       attributeID:null,
       caption:null,
       currentselected:null,                
       currentID:null,
       targetElement:null
    },
    
    initialize: function(options) {
       this.setOptions(options);
       this.process();
    },
    
    // Appel AJAX au module eZ Publish, et Mise à jour de l'élément en fonction de la valeur retournée
    updateAJAX: function(value) {
      var object_id = this.options.currentID;
      
      // Définition de l'URL du module eZ Publish
      var url = this.options.ajaxmodule + '/' + this.options.attributeID + '/' + object_id + '/' + value;
      
      new Request({
       method: 'get',
       url: url,
       
       onSuccess: function(responseText, responseXML) {    
        this.options.targetElement.set('html', responseText);
       }.bind(this)
      }).send();
    },
    
    // Supprime le caption généré
    disposeCaption: function() {
       this.options.targetElement.setStyle('display', '');
       this.options.currentselected = null;
       this.options.caption.getElement().dispose();
    },
    
    //Remplace la zone cible par le caption attendu
    setCaption: function(element) {
       var currentselected = this.options.currentselected;
       
       this.options.currentID = element.getProperty('id').replace(this.options.rootID, '');
       this.options.attributeID = this.options.attributeID;
  
 if (currentselected != element) 
 {
 
   //Vérfie si une balise cible enfant a été définie, sinon exploite la balise courante
   this.options.targetElement = (this.options.targetMatch) ? element.getElement(this.options.targetMatch) : element;
   
   // On restore l'élément précent, si 'change' ou 'blur' non provoqué 
   if (currentselected) {
      var restore_selected = (this.options.targetMatch) ? currentselected.getElement(this.options.targetMatch) : currentselected;
    restore_selected.setStyle('display', '');
   }
   
   //On rend invisible l'élement cible, pour pouvoir le remplacer  
   this.options.targetElement.setStyle('display', 'none');
   
   //On affecte le caption xHTML généré avec la valeur de la page
   this.options.caption.setValue( this.options.targetElement.get('text') );
   
   //On supprime les évènements, par sécurité
   this.options.caption.getElement().removeEvents();
   
   //On affecte les évènements génériques à tous les captions xHTML
   this.options.caption.getElement().addEvents({
   'change': function() { //Sur changement, on update les données 
    var value = this.options.caption.getValue();
    this.updateAJAX(value);
    
    this.disposeCaption()
    
   }.bind(this),
   'blur': function() { //Sur perte du focus 
      this.disposeCaption()
   }.bind(this)
  });
   
   //On injecte le caption après l'élément cible
   this.options.caption.getElement().inject(this.options.targetElement, 'after');
   
   this.options.currentselected = element;
 
 }
 
    },
    
    process: function() {
      //Recherche tous les éléments selon le motif parentMatch, et boucle sur les résultats
      $$(this.options.parentMatch).each(function(element, index) {
   
     var elementID = element.getProperty('id');
     //Teste si les éléments possèdents des ID
     if (elementID && (elementID.indexOf(this.options.rootID)) == 0) {
     
     //Neutralise les liens <a>, pour faciliter l'insertion des captions
     if (element.getProperty('href')) element.setProperty('href', 'javascript:void(0)');;
     
     element.getElements('a').each(function(element_a) {
     element_a.setProperty('href', 'javascript:void(0)');
     });
     
     //Ajoute l'évènement de clic sur tous les éléments matché
     element.addEvents({
   'click': function() {
    this.setCaption(element);
    
   }.bind(this)
  });
     element.setStyles(this.options.styleMatch);
   }
   
      }.bind(this));
 
    }
});
 
 
27/09/2009 10:12 pm (UTC)   Gilles Guirand   View entry   Digg!  digg it!   del.icio.us  del.icio.us

gilles guirand

› Faire de l'édition frontale AJAX avec eZ Publish & Mootools (Partie 2)

Ce billet décrit pas à pas le développement de l'édition frontale dont le fonctionnement est visible sur la vidéo du précédent billet (principe du cliquer / éditer). Le framework JavaScript utilisé est Mootools, dont le fonctionnement est tout à fait similaire à ses principaux concurrents, à savoir : jQuery, Prototype & Script.aculo.us, YUI, Dojo, etc.

Pour ceux qui n'ont jamais utilisés un framework JavaScript, j'en profite pour introduire les objectifs et les fonctionnalités de base que l'on peut trouver dans tous ces frameworks, sans pour autant vous aider à faire un choix... Les critères de choix d'un framework dépendent de bien d'autres critères que leur simple capacités individuelles, comme par exemple :

  • Eviter la multiplication des frameworks sur un même site (ce que l'on constate malheureusement un peu partout)
  • Rester dans la logique de dépendance d'un framework et de son environnement de développement (YUI pour eZ Publish, Dojo pour Zend, Prototype pour Symfony, etc.)
  • Maîtriser un framework correctement, plutôt que 3 frameworks passablement

Introduction aux FrameWorks JavaScript

Un framework JavaScript est écrit en JavaScript, et n'est donc pas plus puissant que le langage lui même. Il s'agit seulement d'un ensemble de librairies et d'extensions du modèle objet qui facilitent l'écriture du code, notamment pour :

  • Manipuler le DOM (atteindre, ajouter, modifier ou supprimer des balises et des attributs dans la page)
  • Manipuler les évènements (évènements de la souris, du clavier)
  • Automatiser des effets complexes (effets de fondus, de Drag & Drop, etc.)
  • Faciliter les appels et traitements AJAX
  • Faciliter la POO, en proposant une syntaxe plus familière (classes, héritage)
  • Masquer certaines incompatibilités de navigateurs

La popularisation de ces frameworks a permis de démocratiser un modèle objet de manipulation du DOM, ainsi qu'une syntaxe grammaticale de sélection de noeuds déclinée du CSS3 Selector (dont voici une liste d'exemple en forme de benchmark pro-Mootools) bien plus lisible et compréhensible que l'API DOM & XPATH du W3C, ce qui génère d'ailleurs des initiatives de portage hors JavaScript comme par exemple : http://code.google.com/p/phpquery/

Spécification de la classe Mootools ajaxwebin

Cette classe a pour vocation de déclarer et d'exécuter le comportement de type "cliquer / éditer" sur un ensemble d'éléments similaires dans la page, comme par exemple :

  • Cas 1 : Remplacer sur clic toutes les balises A contenant une classe CSS 'cuvee_invert', par un champs de saisie
  • Cas 2 : Remplacer sur clic toutes les balises A, positionnées sous une balise TD et contenant une classe CSS commençant par 'stock_', par une liste de déroulante de 1 à 10

Prérequis dans le xHTML :

Les xHTML propulsés doivent être préparés pour faciliter l'identification des éléments éditables, et des correspondances entre les objets et les attributs cibles. Le motif xHTML est la suivante :

<HTMLElement id="rootID_ContentObjectID" class="MatchName" />
 

Par exemple concernant le cas 1 :

<a class="cuvee_invert" id="cuveename_2224"></a>
 

Par exemple concernant le cas 2 :

<td class="td80 stock_3" id="stock_2254"><a href="#">3</a></td>
 

Voici la liste des paramètres et leurs rôles respectifs :

  • ajaxmodule (obligatoire) : Racine du module eZ Publish : ajaxmodule/attributeID/objectID/value
  • parentMatch (obligatoire) : Syntaxe CSS3 (transmise en l'état à Mootools dans un sélecteur) qui permet d'atteindre tous les éléments impactés par l'édition frontale
  • targetMatch (optionnel) : Balise optionnelle à atteindre, comme enfant des éléments résultat du parentMatch (permet par exemple d'atteindre les balises A, enfants des balises TD)
  • styleMatch (optionnel) : Permet de définir un style CSS pour faciliter l'identification des éléments éditables dans la page (bordures, couleurs)
  • rootID (obligatoire) : <HTMLElement id="rootID_ContentObjectID" class="MatchName" />
  • attributeID (obligatoire) : Permet de transmettre le paramètre dans l'URL ajaxmodule/attributeID/objectID/value
  • caption (obligatoire) : Permet d'affecter le masque de saisie personnalisé sur clic, détaillé dans le prochain billet

Instanciation des objets de la classe Mootools ajaxwebin

A noter : les objets instanciés dans la propriété 'caption' seront décrits dans le prochain billet

window.addEvent('domready', function() {
 
    var myajaxwebin = new Array();
    
    // Ajout de la gestion des stocks
    myajaxwebin.push( new ajaxwebin({  
     ajaxmodule: '/ajaxwebin/action',
     parentMatch: 'td[class*="stock_"]',
     targetMatch: 'a',
     styleMatch: {border: '1px dotted #ff0000', background: '#ffffdd'},
     rootID: 'stock_',
     attributeID: 'stock',
     caption: (
        new ajaxcaptionsSelect({  
            'minValue': 0,
            'maxValue': 12,
            'setStyles': {margin: '0 -10px', border: '1px dotted #000', background: '#ffffdd'}
        }))
    }));
    
    // Ajout de la gestion des title des cuvées
    myajaxwebin.push( new ajaxwebin({  
     ajaxmodule: '/ajaxwebin/action',
     parentMatch: 'a[class="cuvee_invert"]',
     styleMatch: {border: '1px dotted #ff0000', background: '#ffffdd'},
     rootID: 'cuveename_',
     attributeID: 'title',
     caption: (
        new ajaxcaptionsInput({  
            'setStyles': {border: '1px dotted #000', background: '#ffffdd'}
        }))
    }));
 
});
 
 

Code commenté de la classe Mootools ajaxwebin

Il est difficile d'expliquer et de décrire l'ensemble du code. Les commentaires devraient être suffisants quant à la compréhension des mécanismes. Cependant voici quelques éléments clés de compréhension :

  • Voir la documentation des classes sur Mootools, et notamment l'utilisation du setOptions
  • Voir la documentation sur bind qui est essentielle à la construction des mécanismes qui mixent la construction dynamique du DOM et l'ajout dynamique d'évènements. Cette méthode permet de modifier la portée du this, pour définir quelle est la valeur du this dans l'exécution des évènements (clics ou autres)
var ajaxwebin = new Class({
    
    Implements: [Options],
 
    options: {
       ajaxmodule: null,
       parentMatch: null,
       targetMatch: null,
       styleMatch: null,
       rootID: null,
       attributeID:null,
       caption:null,
       currentselected:null,                
       currentID:null,
       targetElement:null
    },
    
    initialize: function(options) {
       this.setOptions(options);
       this.process();
    },
    
    // Appel AJAX au module eZ Publish, et Mise à jour de l'élément en fonction de la valeur retournée
    updateAJAX: function(value) {
      var object_id = this.options.currentID;
      
      // Définition de l'URL du module eZ Publish
      var url = this.options.ajaxmodule + '/' + this.options.attributeID + '/' + object_id + '/' + value;
      
      new Request({
       method: 'get',
       url: url,
       
       onSuccess: function(responseText, responseXML) {    
        this.options.targetElement.set('html', responseText);
       }.bind(this)
      }).send();
    },
    
    // Supprime le caption généré
    disposeCaption: function() {
       this.options.targetElement.setStyle('display', '');
       this.options.currentselected = null;
       this.options.caption.getElement().dispose();
    },
    
    //Remplace la zone cible par le caption attendu
    setCaption: function(element) {
       var currentselected = this.options.currentselected;
       
       this.options.currentID = element.getProperty('id').replace(this.options.rootID, '');
       this.options.attributeID = this.options.attributeID;
  
 if (currentselected != element) 
 {
 
   //Vérfie si une balise cible enfant a été définie, sinon exploite la balise courante
   this.options.targetElement = (this.options.targetMatch) ? element.getElement(this.options.targetMatch) : element;
   
   // On restore l'élément précent, si 'change' ou 'blur' non provoqué 
   if (currentselected) {
      var restore_selected = (this.options.targetMatch) ? currentselected.getElement(this.options.targetMatch) : currentselected;
    restore_selected.setStyle('display', '');
   }
   
   //On rend invisible l'élement cible, pour pouvoir le remplacer  
   this.options.targetElement.setStyle('display', 'none');
   
   //On affecte le caption xHTML généré avec la valeur de la page
   this.options.caption.setValue( this.options.targetElement.get('text') );
   
   //On supprime les évènements, par sécurité
   this.options.caption.getElement().removeEvents();
   
   //On affecte les évènements génériques à tous les captions xHTML
   this.options.caption.getElement().addEvents({
   'change': function() { //Sur changement, on update les données 
    var value = this.options.caption.getValue();
    this.updateAJAX(value);
    
    this.disposeCaption()
    
   }.bind(this),
   'blur': function() { //Sur perte du focus 
      this.disposeCaption()
   }.bind(this)
  });
   
   //On injecte le caption après l'élément cible
   this.options.caption.getElement().inject(this.options.targetElement, 'after');
   
   this.options.currentselected = element;
 
 }
 
    },
    
    process: function() {
      //Recherche tous les éléments selon le motif parentMatch, et boucle sur les résultats
      $$(this.options.parentMatch).each(function(element, index) {
   
     var elementID = element.getProperty('id');
     //Teste si les éléments possèdents des ID
     if (elementID && (elementID.indexOf(this.options.rootID)) == 0) {
     
     //Neutralise les liens <a>, pour faciliter l'insertion des captions
     if (element.getProperty('href')) element.setProperty('href', 'javascript:void(0)');;
     
     element.getElements('a').each(function(element_a) {
     element_a.setProperty('href', 'javascript:void(0)');
     });
     
     //Ajoute l'évènement de clic sur tous les éléments matché
     element.addEvents({
   'click': function() {
    this.setCaption(element);
    
   }.bind(this)
  });
     element.setStyles(this.options.styleMatch);
   }
   
      }.bind(this));
 
    }
});
 
 
27/09/2009 10:12 pm (UTC)   Gilles Guirand   View entry   Digg!  digg it!   del.icio.us  del.icio.us

ez projects

› Noven Image Cropper 1.0 final released !

Version 1.0 has been released. Check it out ! :-)

27/09/2009 11:18 am (UTC)   eZ Projects   View entry   Digg!  digg it!   del.icio.us  del.icio.us

thomas koch

› Timeline of PHP quality assurance tools

I'll be giving a talk on PHP quality assurance tools next week at the gearconf in Düsseldorf. The event is focused on team collaboration and most of the audience will come from other languages then PHP. Since I'm not a contributor to any QA tool I'll center around the user perspective and the process of establishing these tools in a small to middle web company like YMC.
As an outline for the first part I thought to give an historical view on QA in PHP and therefor made a timeline of PHP QA milestones:
The dates as text:
  • ~1997 JUnit
  • 6.4.2002 PHPUnit 0.1
  • 28.8.2003 PHPUnit 1.0.0-alpha1 Release
  • 13.7.2004 PHP5.0.0
  • 14.7.2004 release PHPUnit2
  • 30.1.2006 eZ Components 1.0
  • ~sept 2006 ZF uses PHPUnit (issue #374)
  • 4.10.2006 first integration of Selenium in PHPUnit
  • 1.7.2007 Zend Framework 1.0
  • 18.11.2007 first release of PHPUnderControl
  • 19.11.2007 Project Mess Detection in PHPUnit
  • 6.2.2008 first release PHP_Depend
  • march 2009 qualityassuranceinphpprojects.com, thePHP.cc
main sources:

Did I get all this right? Are there things I should have added?
26/09/2009 4:58 pm (UTC)   Thomas Koch   View entry   Digg!  digg it!   del.icio.us  del.icio.us

ez projects

› Translations

Translation contributions are welcome !

24/09/2009 2:43 pm (UTC)   eZ Projects   View entry   Digg!  digg it!   del.icio.us  del.icio.us

ez projects

› Noven Image Cropper 1.0 RC1 released !

Version 1.0 RC1 has been released. Please give feedback :-)

24/09/2009 2:39 pm (UTC)   eZ Projects   View entry   Digg!  digg it!   del.icio.us  del.icio.us

community news (ez.no)  eZ systems employee

› eZ Publish 4.2.0-rc2 released

After last week's rc1 release, we are pleased to provide you the second and final release candidate of the upcoming major release of eZ Publish 4.2.0 next tuesday. An additional 20+ issues are resolved in the eZ Publish kernel and associated extensions. This is also the (short) last community testing opportunity for eZ Publish 4.2.0 while it undergoes also a frantic internal QA procedure.

23/09/2009 9:19 pm (UTC)   Community news (ez.no)   View entry   Digg!  digg it!   del.icio.us  del.icio.us

mugo web

› Multi-class single attribute fetch for eZ Publish

It is a reasonably common use case to fetch a list of content objects based on a common attribute implemented in multiple content classes. For example, to fetch all articles, blog posts and comments published by a given author. Certainly one could do multiple fetches and then massage the result sets together, but that's a lot of work. In this post I'll create a custom fetch function to do the fetch with one SQL query. And shine some light on the eZ Publish db schema around objects, along the way.

21/09/2009 9:25 pm (UTC)   Mugo Web   View entry   Digg!  digg it!   del.icio.us  del.icio.us