/extension/myblog/design/myblog/stylesheets/white.css
/extension/myblog/design/myblog/stylesheets/black.css

Select your style :

A la une // Les blogs sur le développement Web, l'oenologie, Montpellier, etc...

Faire un Widget Netvibes & iGoogle avec UWA, AJAX & eZ Publish : La couche eZ Publish (Partie 2)

Après une introduction aux mécanismes d'UWA, ce billet décrit comment produire les différents contenus d'eZ Publish, dans une perspective de navigation AJAX au sein du Widget, et comment les gérer côté UWA.

Pour illustrer le mécanisme, je vais décrire la chaîne complète de fabrication pour la fonctionnalité de "recherche", sur l'onglet "ma cave". Le point central du développement réside dans la logique des URL et des appels AJAX, qui facilite le développement, et qui favorise la navigation full AJAX au sein du Widget. Le principe peut ensuite se décliner sur toutes autres fonctionnalités équivalentes.

Autant utiliser un exemple qui représente parfaitement la genèse de ce Blog, la recherche de 'joly', qui permet d'atteindre le domaine Virgile Joly (producteur de merveilleux vins Bio en Languedoc), et qui permet également d'illustrer un exemple d'approximation syntaxique (jolie) grâce à eZ Find.

Le choix d'un mécanisme AJAX

Concernant la fourniture des données pour les appels AJAX, eZ Publish propose (au moins) 4 mécanismes :

  • Utiliser la directive /layout/set/, comme pour la gestion de mes commentaires. Cette technique est inadaptée à ma problématique, puisqu'elle permet uniquement de définir un autre pagelayout.tpl, sans altérer les autres vues.
  • Utiliser un override du pagelayout.tpl en fonction de divers paramètres, comme par exemple pour mes RSS sur mesure par keyword. Cette technique est exploitée pour la gestion des derniers commentaires (2ième onglet du Widget), et pourrait fonctionner pour la rubrique ma cave. Elle reste cependant assez consommatrice de ressources, et peu flexible.
  • Utiliser un siteaccess différent, avec son design, son pagelayout.tpl et son jeu de templates. C'était faisable, mais je n'ai pas opté pour ce mécanisme.
  • Utiliser un module central, permettant de ventiler les paramètres, et de charger les différents templates en fonction des différents affichages (recherche, home, région, appellation, domaine, cuvée, etc.). C'est la méthode que j'ai adoptée, dans un soucis de maîtrise parfaite du comportement du Widget.

Création d'un module eZ Publish Widget

La logique d'appel des URL du module est la suivante : /widget/action/[action]/[param], par exemple :

On remarquera qu'il est ensuite possible de naviguer entre les différentes vues, avec cette même logique de liens. En quelques sorte, les vues épurées pour les chargement AJAX sont autonomes est peuvent être parcourues par hyperliens, comme leurs devenir au sein du Widget. Le dispositif devient ainsi très productif pour tester ses vues, sans obligation d'un chargement AJAX.

Définition du module eZ Publish Widget

<?php
$Module = array( 'name' => 'Widget Gandbox.fr' );
 
$ViewList = array();
$ViewList['action'] = array( 'script' => 'widget.php',
                           'default_navigation_part' => 'widget',
                           'ui_context' => 'view',
                           'params' => array ( 'action', 'param' )
      );
 
$FunctionList = array();
?>
 
 

Implémentation du module eZ Publish Widget

Pour simplifier la lecture, cet extrait de code se concentre uniquement sur la recherche

<?php
require_once( 'kernel/common/template.php' );
$tpl = templateInit();
 
$http = eZHTTPTool::instance();
 
$Module = $Params["Module"];
 
if ( isset( $Params['param'] ) )
{
 $param = trim(urldecode($Params['param']));
} else {
 $param = '';
}
 
$tpl->setVariable( 'param', $param);
 
$Result = array();
$Result['path'] = array();
$Result['pagelayout'] = 'design:widget_pagelayout.tpl';
 
if ($param != '') {
 
 switch ($Params['action']) {
 
  case "search":
   $Result['content'] = $tpl->fetch('design:widget/widget_cave_search.tpl');
 
   break;
  //case autres actions...
 }
 
} else {
 $Result['content'] = '';
}
 
?>
 
 

La création des templates

Restons sur l'exemple de la recherche, avec le template 'widget_cave_search.tpl'

{def $siteurl = concat('http://',ezini('SiteSettings', 'SiteURL'))}
{def $search=fetch( 'content', 'search',
                    hash( 'text', $param,
                          'class_id', array( '28', '29' ),
     'limit', 10
     ) )}
 
{def $search_count = $search.SearchCount}
{def $search_result_label = 'Aucun résultat'}
 
{if $search_count|gt(0)}
 {set $search_result_label = concat('<strong>', $search_count, '</strong> résultat(s) pour <strong>"', $param, '</strong>"')}
{/if}
 
<div id="search_result_label">{$search_result_label}</div>
 
<div id="search_results">
{foreach $search.SearchResult as $item}
 <div class="search_line">
 {switch match=$item.class_identifier}
  {case match='cave_domaine'}
  <div class="search_line_info"><a class="ajax" href="{$siteurl}/widget/action/domaine/{$item.node_id}">{$item.name|wash}</a> / {$item.parent.name|wash} ({$item.score_percent}%)</div>
  <div class="search_line_highlight">{$item.highlight}</div>
  {/case}
  {case match='cave_cuvee'}
  <div class="search_line_info"><a class="ajax" href="{$siteurl}/widget/action/cuvee/{$item.node_id}">{$item.name|wash}</a> / <a href={$siteurl}/widget/action/domaine/{$item.node_id}>{$item.parent.name|wash}</a> ({$item.score_percent}%)</div>
  <div class="search_line_highlight">{$item.highlight}</div>
  {/case}
 {/switch}
 </div>
{/foreach}
</div>
{undef $search $search_count $search_result_label $siteurl}
 
 

A noter : L'utilisation de la classe CSS 'ajax', qui me permet dans les appels AJAX, de distinguer les liens internes (à provoquer en AJAX pour un affichage dans le Widget) et les liens externes.

L'appel AJAX de la recherche côté UWA

L'appel AJAX côté UWA fonctionne selon le processus suivant :

  • Créer l'URL d'appel de la recherche, et récupérer le résultat
  • Transformer les liens pour provoquer les nouveaux appels AJAX (liens internes) et les appels externes (liens externes)
  • Injecter le résultat dans un conteneur
// --------------------------
// Classes de gestion de ma Cave
// --------------------------
var myCaveSearch = {};
 
//injection du résultat XHTML
myCaveSearch.setAJAXcontent = function(text)
{
 myAJAXContainer.setHTML(text);
}
 
//Appel de l'URL de recherche, traitement du résultat dans dataProcessor
myCaveSearch.dataInit = function(text) {
 
 UWA.Data.getText(
     _URL_PROXY+_URL_GANDBOX+'/widget/action/search/'+encodeURIComponent(text)+'/',
     myCaveSearch.dataProcessor
 );
}
 
//Traitement du résultat de l'appel AJAX
myCaveSearch.dataProcessor = function(text) {
 myCaveSearch.setAJAXcontent(text);
  
 // Récupération de tous les liens présents dans le code XHTML
 var links = myAJAXContainer.getElements('a');
  
 for (var i = 0, lnk; lnk = links[i]; i++) {
   
  //Si le lien contient la class CSS ajax, hack IE : className
  if ( (lnk.getAttribute('className') == 'ajax') || lnk.hasClassName('ajax') ) {
   lnk.myAJAXlink = lnk.href;
    
   //Création du lien pour l'appel AJAX
   lnk.onclick = function() {
    myCaveSearch.linkAJAX(this.myAJAXlink);
   }
   lnk.href = 'javascript:void(0)';
  }
  else {
   lnk.target = '_blank';
   if (lnk.href.indexOf('http://') == -1)
    {lnk.href = _URL_GANDBOX+lnk.getAttribute('href');}
  }
 
 }
}
 
//Action sur un lien AJAX, avec image de chargement
myCaveSearch.linkAJAX = function(url) {
 myCaveSearch.setAJAXcontent(_XHTML_LOADER);
  
 UWA.Data.getText(_URL_PROXY+url, myCaveSearch.dataProcessor);
}
 
//Récupération du champs de recherche, appel de la fonction de chargement AJAX
myCaveSearch.Cave_getSearch = function()
{
 var search = widget.body.getElementsByClassName('gandbox_search_input')[0];
  
 if (search.value.trim() == '') {
  search.setStyle('border', '1px solid #ff0000');
 } else
 {
  myCaveSearch.setAJAXcontent(_XHTML_LOADER);
  myCaveSearch.dataInit(search.value);
  search.setStyle('border', '1px solid #888888');
 }
  
}
 
 

Cet extrait de code n'est pas complet, mais contient l'essentiel. Pour en savoir plus il suffit d'inspecter le code complet du Widget.

A noter : la variable _XHTML_LOADER contient le code XHTML de préchargement, permettant d'afficher une jolie animation fabriquée à partir du site http://www.ajaxload.info/

Que boire avec ce billet ?

Domaine Virgile Joly - Virgile - Rouge 2003

Région : Languedoc
Appellation : Coteaux du Languedoc
Domaine : Domaine Virgile Joly
Couleur :
 
Stock : 0
Notation :
Prix : 25 €
Commentaire(s) : 2 Commentaire(s)

Malgré une année délicate, ce millésime impressionne par se sensation de fraîcheur naturelle, comme tous les vins du domaine. L'équilibre entre les arômes, le bois et le fruit est étonnant, et la garde difficile... pas à cause de son potentiel, mais parcequ'il est difficile d'attendre ! C'est le genre de bouteille que l'on a envie d'ouvrir encore et encore...

Publié par : Beegee, le 28 Juillet 2009 11:20 am

Sources du Widget ?

Salut Monsieur;

Est-il possible d'avoir le code source de ce widget sous forme de répertoire zippé ?

C'est vraiment fantastique comme contribution. J'aimerais avoir cette source svp.


Merci

Publié par : Gandbox, le 28 Juillet 2009 06:12 pm

Ou trouver le code du Widget

Voir la première partie du billet

Publié par : Beegee, le 01 Août 2009 11:49 am

ajaxProxy.php ???

Salut Gilles,
Il n'y a pas les sources de "ajaxProxy.php" et même le css contenant la classe "ajax" n'est pas dans les sources fournies !

J'aimerais avoir les sources de ce gadget.

Merci d'avance pour ce grand boulot !

Publié par : gandbox, le 01 Août 2009 02:49 pm

AjaxProxy

Concernant le fichier ajaxProxy.php, 2 liens sont proposés dans la première partie de ce billet.

Concernant la classe "ajax", libre à toi de créer un CSS correspondant si nécessaire, par défaut c'est inutile puisque cette classe sert uniquement de cible aux appels JavaScript, et n'a pas vocation à définir un style.

Publié par : Beegee, le 03 Août 2009 09:07 am

... et pour les Vues Ez publish ?

Merci, pour ajaxProxy.php et ajax. J'ai travaillé dessus depuis hier et ce qui me manque maintenant, c'est vraiment les vues ez publish. c'est encore un peu flou à mes analyses. J'ai le doc learning ez publish 3. Building content management solutions, et les vues là, ce doit être génial. J'ai même lu sur le site ez.no. Mais ce que je veux c'est faire une extension (module) avec des vues. Or il n'existe pas d'exemple en tant qu'extension (module) dans ez publish. Et la magie de cette extension, c'est qu'elle produit ses données à destination de n'importe où.

Stp, si je peux avoir les sources des vues "domaine", "region", "cave" avec comment le css est structuré dans chacune d'elles, je crois que ce sera la solution totale.

Mais avant tout ça, je te dis encore mille fois merci pour ce très grand boulot. Je n'étais pas du tout au courant de l'uwa (netvibes) jusqu'à ce que je tombe sur ce génial chef d'oeuvre. Et c'est exactement ce que je voulais pour faire avancer les travaux du projet sanagha.net

Merci Gilles

Publié par : gandbox, le 03 Août 2009 08:49 pm

Construire un module ?

@Beegee :
Concernant la fourniture de templates ou codes complet, c'est un peu contraire à mon objectif sur le Blog, à savoir : détailler des fonctionnements clés et des mécanismes pour apprendre à faire par soit même, et non pas proposer de simple "copier / coller".

Concernant le fonctionnement des vues "domaine", "region", "cave", il s'agit de simple templates, sur le même modèle que "search". Il suffit donc d'ajouter le "case region" dans le module à la suite du "search", et de créer le template widget/widget_region.tpl. Ensuite le template doit contenir des liens vers le module ({$siteurl}/widget/action/region/{$item.node_id}) sur le même modèle que "search", avec la fameuse classe "ajax".

C'est peut être l'utilisation de la classe "ajax" qui te perturbe ? Cette classe CSS ne sert à pas à styler les éléments, mais permet à javascript de cibler les liens qui devront etre remplacer automatiquement dans le widget par un appel AJAX (voir la méthode linkAJAX dans mon billet).