ez projects

› nvNewsletter 0.7b

Version 0.7b of nvNewsletter published. This update includes many bug fixes, changes and new features. Check doc/changelogs.

26/05/2010 10:32 am (UTC)   eZ Projects   View entry   Digg!  digg it!   del.icio.us  del.icio.us

david linnard

› Using Custom PHP Functions in eZ Publish Templates

In a previous post, I discussed how to use standard PHP functions within eZ Publish Templates. This article demonstrates how to use custom functions instead using custom Template Operators. What is a Template Operator? Template operators are the standard tags you use in the templates of eZ Publish, for example, the following are all template operators (a [...]
23/05/2010 9:15 pm (UTC)   David Linnard   View entry   Digg!  digg it!   del.icio.us  del.icio.us

gilles guirand

› Développement avancé avec eZ Find (partie 3 : Tirer profit de la syntaxe Solr)

Le billet précédent décrit comment ajouter des champs supplémentaires dans Solr, afin de pouvoir les exploiter avec la syntaxe native d'eZ Find sous la forme 'mycontentclass/mycontentattribute/mycontentsubattribute'.

Cette syntaxe spécifique à eZ Find est certes confortable mais non exclusive, à savoir qu'il est possible de mixer des éléments de syntaxes eZ Find et des éléments de syntaxes Solr, comme par exemple les noms des champs ('attr_myfield_type') ou encore des opérateurs logiques (AND, NOT, etc.).

- OUI c'est une mauvaise pratique. Une syntaxe 'interface' n'est pas faites pour être outrepasser, au risque de compromettre l'évolutivité de la couche basse, à savoir Solr
- OUI cela peut faciliter les développements, voir même sauver la vie du développeur sur certaines situations complexes

Ce billet montre quelques exemples d'exploitation de la syntaxe Solr, volontairement simplifiés pour en faciliter la compréhension.

Faire un tri sur un attribut commun à plusieurs classes

Il s'agit d'un grand classique des problématiques d'eZ Publish, à savoir :

  • On crée 2 classes différentes pour X raisons "Post" et "Article"
  • On ajoute dans chacune des classes des attributs communs, parceque utile dans les 2 cas, par exemple un attribut "Date"

Résultat : Impossible de mélanger les "Post" et "Article" en les triant par dates décroissante (sauf à développer un terrifiant opérateur de template). Généralement, les développeurs font en sorte d'utiliser une seule classe plus générique afin de contourner ou plutôt déplacer le problème (la mutualisation d'une classe peut avoir d'autres inconvénients).

La solution des attributs communs entre les classes avec eZ Find :

Le premier billet de cette série consacré à eZ Find décrit la logique de nommage des champs dans Solr. Une effet secondaire positif de cette convention (lié au concept de dynamicfields de Solr) est l'absence bienheureuse de l'identifiant de la classe dans le nom de chaque champs. Il est ainsi possible d'exploiter les champs homonymes comme bon nous semble, au travers de recherche, filtres ou tris en fonction du besoin.

Exemple de code de template eZ Publish sur la seule classe "Post" :

{def $search_result = fetch( 'content', 'list', hash( 'parent_node_id', 2,
    'class_filter_type',  'include',
    'class_filter_array', array(24),
    'sort_by', array( array( 'attribute', false(), 'post/date' ) ),
    'limit', 10,
    'depth', 3
))}
 

Exemple équivalent de code de template eZ Find pour notre problématique de tri inter-classes "Post" et "Article" :

{def $search=fetch( ezfind, search,
     hash( query , '',
           'class_id', array('post', 'article'),
           'limit', 10,
           'sort_by', hash('attr_date_dt', 'desc')
))}
 

A noter : une évolution souhaitable d'eZ Find serait de pouvoir exploiter une syntaxe du type '//date', afin de rendre facultatif l'ajout de la classe dans le filtre Solr généré.

Travailler avec les keywords

Contrairement à l'exemple précédent sur les dates, les keywords disposent dans eZ Publish d'une table externe au contenu (ezkeyword_attribute_link), permettant de lier chaque keyword à divers contenus de diverses classes. Cependant le fetch par keyword ne dispose pas de tous les filtres possibles d'un fetch par list par exemple (class_filter_type, class_filter_array, extended_attribute_filter, etc.). Cette limitation se comprend puisque permettre un filtre inter-classes engendre forcement une limitation sur les fonctionnalités liées aux attributs spécifiques à chaque classes.

Sur le même logique que le traitement par date, on peut donc exploiter eZ Find pour effectuer toutes les opérations nécessaires autour des keywords. Voici un exemple de code :

'filter', array('attr_tags_lk:"ez publish"', 'NOT attr_title_t:"RSS"')
 

Résultat : Retourne uniquement les résultats associés au keyword "eZ Publish" ou "ez publish" (notez l'utilisation du _lk pour lowercase), ne contenant pas "RSS" dans le titre

'filter', array('attr_tags_lk:"ez publish"', 'attr_tags_lk:"mootools"')
 

Résultat : Retourne uniquement les résultats associés à la fois au keyword "eZ Publish" ou "ez publish", et au keyword "Mootools" ou "mootools"

Faire des filtres complexes

Voici quelques exemples de filtres, qui n'ont rien d'exhaustif puisqu'il est possible d'exploiter l'ensemble des opérateurs Lucene en fonction de la version de Solr déployée (version de Solr 1.4, disponible dans eZ Find 2.2 lors de la rédaction de ce billet).

'filter', array('NOT ( attr_title_t:(ez+find) OR attr_intro_t:(ez+find) )')
 

Résultat : Retourne uniquement les résultats qui possèdent l'expression 'ez find' ou 'eZ Find', dans l'attribut 'title' ou l'attribut 'Intro'. Il faut noter l'utilisation de la version 'text' (_t) sur l'attribut 'title' permettant de profiter du non respect de la casse (contrairement au type 'string').

'filter', array('attr_title_s:[A TO G] AND ezf_df_text:google~0.7')
 

Résultat : Retourne uniquement les résultats dont le 'title' commence par A,B,C,D, E ou F (G exclu), et dont le contenu possède approximativement l'expression 'google' (Google, iGoogle, etc.).

  • A noter : Le ratio '0.7' peut être ajuster au besoin
  • A noter : le champs 'ezf_df_text' est un champs constitué dynamiquement et par recopie de tous les autres champs de types 'string', 'text' ou 'keyword'. On peut aussi utiliser le champs 'ezf_sp_words' si la fonctionnalité de spellcheck est exploitable. Voir le fichier schema.xml et la définition de ces champs par copyField pour plus de détail.
23/05/2010 7:17 pm (UTC)   Gilles Guirand   View entry   Digg!  digg it!   del.icio.us  del.icio.us

david linnard

› Useful eZ Publish Links

Here are some links I’ve come back to quite a few times while developing eZ Publish based sites. Some of the things they tackle are incredibly useful and are hard to track down. PHP Scripting (for Cronjobs and Other Scripts) Running Command Line Scripts Fetching Class Type in PHP Fetching eZ publish content objects with PHP Running Cronjobs manually An Introduction [...]
23/05/2010 12:06 am (UTC)   David Linnard   View entry   Digg!  digg it!   del.icio.us  del.icio.us

ez projects

› eZ Tika 1.3 released (binary file indexing)

Many bug fixes and quality enhancements to existing converters, most notably CJK pdf documents are now correctly converted to UTF-8 code.

18/05/2010 11:46 pm (UTC)   eZ Projects   View entry   Digg!  digg it!   del.icio.us  del.icio.us

ez projects

› ezqrcode 1.0 alpha2 released

This is one more alpha release for the upcoming stable version.

18/05/2010 5:54 pm (UTC)   eZ Projects   View entry   Digg!  digg it!   del.icio.us  del.icio.us

ez projects

› 0.5 Beta released

eZ Toolbox is updated to 0.5 to fix a problem with last buid of Chrome.
If you already have the extension installed it will be automatically updated.

17/05/2010 1:24 pm (UTC)   eZ Projects   View entry   Digg!  digg it!   del.icio.us  del.icio.us

gilles guirand

› Développement avancé avec eZ Find (partie 2 : Indexer des champs supplémentaires dans Solr)

Le billet précédent décrit les mécanismes bas niveaux d'eZ Find, et la façon dont les correspondances entre les attributs eZ Publish (noms, types de champs) et les champs Solr sont gérés. Ce billet décrit comment eZ Find peut considérablement faciliter le développement de certaines fonctionnalités (en évitant de complexes opérateurs de templates aux multiples requêtes SQL...), en ajoutant automatiquement des champs dans Solr lors de l'indexation d'un contenu, ré-exploitables par la suite pour la construction d'une facette par exemple ou pour profiter d'un filtre supplémentaire.

Etude de cas : un filtre par années, et par mois-années

L'exemple étudié a surtout une valeur pédagogique, puisqu'il s'agit d'un besoin générique et relativement simple à implémenter. Les listes d'actualités, ou les listes de billets sur les Blogs proposent généralement des filtres par années (2010) ou par mois / année (Janvier 2010) (comme sur ce blog par exemple dans la colonne de droite). Habituellement, pour ce genre de filtre on développe un opérateur de template permettant d'effectuer la requête SQL nécessaire, ce qui peut rapidement devenir très complexe. Il suffit de lire le code de l'opérateur de template eZArchive pour en comprendre les limites.

Il est relativement fréquent que ces manipulations SQL souffrent de carences fonctionnelles, comme la propagation des droits, la gestion des langues, la gestion de certaines subtilités entre MySql ou PostGreSql. Ce sont des problématiques à la charge du développeur, puisque l'on contourne les API pour exploiter directement du SQL. Par ailleurs, l'opérateur eZArchive montre une autre limite importante, puisqu'il se contente de travailler sur la date de publication 'publication_date' (par facilité), et ne permet donc pas d'exploiter un attribut de date spécifique à la classe.

Développer le filtre par années, et par mois-années avec eZ Find

Etape 1 : Indexer les années, mois / année vers Solr

Les settings d'eZ Find (ezfind.ini, à surcharger dans le ezfind.ini.append.php de votre extension) permettent de déléguer le traitement de l'indexation d'un datatype eZ Publish vers une classe PHP :

[SolrFieldMapSettings]
CustomMap[ezdate]=ezfSolrDocumentFieldDate
 

Notre classe PHP, nommée arbitrairement ezfSolrDocumentFieldDate hérite de la classe ezfSolrDocumentFieldBase et doit être ajoutée dans le dossier /extension/myextension/classes/ezfsolrdocumentfielddate.php avec le squelette suivante :

<?php
class ezfSolrDocumentFieldDate extends ezfSolrDocumentFieldBase
{
 public static function getFieldName( eZContentClassAttribute $classAttribute, $subAttribute = null, $context = 'search' )
 {
 // return the fieldname like : attr_mydate_d
 }
 
 public function getData()
 {
 // return the array keys (fieldname => value), like : array('attr_mydate_dt' => '2010-04-30T00:00:00Z')
 }
}
?>
 

Le rôle de la méthode getFieldName

Cette méthode est invoquée pour transformer les noms des attributs eZ Find vers les noms de champs Solr. Ainsi lorsqu'on construit une facette avec la syntaxe 'mycontentclass/mydateattribute', cette méthode reçoit 'mydateattribute' et doit retourner 'attr_mydateattribute_dt'. Nous allons donc implémenter cette fonction de la façon suivante :

  • Si un sous attribut est spécifié (par exemple 'mycontentclass/mydateattribute/year'), alors retourne le nom composé du sous attribut
  • Si aucun sous attribut n'est spécifié, alors exécute le code de la classe parent
  • Important : Pour profiter d'une certaine généricité, et éviter de nommer 'en dur' les champs Solr, nous utilisons les méthodes parents generateSubattributeFieldName et generateAttributeFieldName
const DEFAULT_SUBATTRIBUTE_TYPE = 'date';
 
public static function getFieldName( eZContentClassAttribute $classAttribute, $subAttribute = null, $context = 'search' )
{ 
  switch ( $classAttribute->attribute( 'data_type_string' ) )
  {
    case 'ezdate' :
    {
     if ( $subAttribute and $subAttribute !== '' )
     {
      // A subattribute was passed
      return parent::generateSubattributeFieldName( $classAttribute,
       $subAttribute,
       self::DEFAULT_SUBATTRIBUTE_TYPE );
     }
     else
     {
      // return the default field name here.
      return parent::generateAttributeFieldName( $classAttribute, self::getClassAttributeType( $classAttribute, null, $context ) );
     }
  } break;
    
    default:
    {} break;
  }
}
 

Le rôle de la méthode getData

Cette méthode est invoquée pour extraire les données d'eZ Publish, et les préparer pour leur indexation vers Solr. C'est donc dans cette méthode que l'on peut ajouter nos champs supplémentaires 'year' et 'yearmonth'. Pour faciliter la future exploitation de ces champs avec eZ Find, je souhaite pouvoir construire des facettes ou des filtres selon la syntaxe classique :

  • 'mycontentclass/mydateattribute/year', dont la transposition Solr serait 'subattr_date-year_dt'
  • 'mycontentclass/mydateattribute/yearmonth', dont la transposition Solr serait 'subattr_date-yearmonth_dt'
public function getData()
 {
  $contentClassAttribute = $this->ContentObjectAttribute->attribute( 'contentclass_attribute' );
 
  switch ( $contentClassAttribute->attribute( 'data_type_string' ) )
  {   
  case 'ezdate' :
  {
  $returnArray = array();
   
  // Get timestamp attribute value
  $value = $this->ContentObjectAttribute->metaData();
   
  // Generate the main filedName attr_XXX_dt 
  $fieldName = parent::generateAttributeFieldName( $contentClassAttribute,
  self::DEFAULT_ATTRIBUTE_TYPE );
 
  $returnArray[$fieldName] = parent::convertTimestampToDate( $value );
 
  // Generate the yearmonth subattribute filedName subattr_year_dt
  $fieldName = parent::generateSubattributeFieldName( $contentClassAttribute,
  'year',
  self::DEFAULT_SUBATTRIBUTE_TYPE );
 
  $year = date("Y", $value); // Get Year value : 2010
  $returnArray[$fieldName] = parent::convertTimestampToDate( strtotime($year.'-01-01') );
 
  // Generate the yearmonth subattribute filedName subattr_yearmonth_dt
  $fieldName = parent::generateSubattributeFieldName( $contentClassAttribute,
  'yearmonth',
  self::DEFAULT_SUBATTRIBUTE_TYPE );
 
  $month = date("n", $value); // Get Month value : 3
  $returnArray[$fieldName] = parent::convertTimestampToDate( strtotime($year.'-'.$month.'-01') );
   
  return $returnArray;
   
  } break;
 
  default:
  {} break;
  }
 }
}
 

A noter : $returnArray contient un tableau à clés, dont voici un exemple de sortie (effectuer avec var_dump) :

array(3) {
 ["attr_date_dt"]=>
 string(24) "2008-12-28T00:00:00.000Z"
 ["subattr_date-year_dt"]=>
 string(24) "2008-01-01T00:00:00.000Z"
 ["subattr_date-yearmonth_dt"]=>
 string(24) "2008-12-01T00:00:00.000Z"
}
 

A noter : Solr utilise le format de date ISO 8601, du type '2010-04-30T00:00:00Z'. La classe parent ezfSolrDocumentFieldBase propose la méthode convertTimestampToDate() pour convertir un format timestamp vers un format ISO 8601.

Le template de construction des facettes

Nos données par années et par mois-année sont maintenant disponibles. Il ne reste plus qu'à construire nos facettes avec la syntaxe habituelle :

{def $search_yearmonth=fetch( ezfind, search,
 hash( query , '',
 'facet', array( 
 hash('field', 'billet/date/year', 'sort', 'alpha', 'limit', 20 ),
 hash('field', 'billet/date/yearmonth', 'sort', 'alpha', 'limit', 20 )
 ),
 'class_id', array('billet'),
 'subtree_array', array(2)
 ))}
 
 {def $search_extras_year=$search_yearmonth['SearchExtras'].facet_fields[0].nameList|reverse}
 {def $search_extras_yearmonth=$search_yearmonth['SearchExtras'].facet_fields[1].nameList|reverse}
 {def $date_count = 0
 $date_ts = 0}
 
<li id="blog_block_10" class="colonne_block">
 <h1>Archives par années :</h1>
 <ul class="{$current_css} list">
 {foreach $search_extras_year as $facetID =&gt; $datevalue}
  {set $date_count = $search_yearmonth['SearchExtras'].facet_fields[0].countList[$facetID]}
  {set $date_ts = $datevalue|strtotime}
 <li><a href={concat('/Blogs/(year)/',$date_ts|datetime( 'custom', '%Y' ))|ezurl} title="Archives : {$date_ts|datetime( 'custom', '%Y' )} // {$date_count} Billet(s)">{$date_ts|datetime( 'custom', '%Y' )}</a></li>
 {/foreach}
 </ul>
</li>
<li id="blog_block_11" class="colonne_block">
 <h1>Archives par mois / années :</h1>
 <ul class="{$current_css} list">
 {foreach $search_extras_yearmonth as $facetID =&gt; $datevalue}
  {set $date_count = $search_yearmonth['SearchExtras'].facet_fields[1].countList[$facetID]}
  {set $date_ts = $datevalue|strtotime}
 <li><a href={concat('/Blogs/(year)/',$date_ts|datetime( 'custom', '%Y' ),'/(month)/',$date_ts|datetime( 'custom', '%n' ))|ezurl} title="Archives : {$date_ts|datetime( 'custom', '%F %Y' )} // {$date_count} Billet(s)">{$date_ts|datetime( 'custom', '%F %Y' )|upfirst}</a></li>
 {/foreach}
 </ul>
 
</li>
{undef $date_ts $date_count $search_yearmonth $search_extras_yearmonth}
 

A noter : Le fetch proposé est relativement basique, afin de faciliter la compréhension du mécanisme. Il faut bien sur faire évoluer le code pour obtenir le fonctionnel attendu (utiliser les filtres par exemple), mais ce n'est pas l'objet de ce billet puisque la documentation officielle détaille déjà la façon de procéder.

A noter : le 'sort', 'alpha' ne permet pas réellement de spécifier que l'on souhaite un tri alphabétique. Il s'agit surtout de spécifier que l'on ne souhaite pas un tri par 'count' (nombre d'items associés à la facette). Dans ce cas Solr applique un tri automatique 'croissant' en fonction de son index et du type de donnée (ce qui explique l'utilisation de l'opérateur reverse pour obtenir une liste décroissante).

16/05/2010 3:30 pm (UTC)   Gilles Guirand   View entry   Digg!  digg it!   del.icio.us  del.icio.us

ez projects

› Updated documentation

The documentation of the abpdfcatalogue extension has been updated by adding a new chapter about how to customize the common or a dedicated PDF catalogue.

The documentation can be found here: http://www.alexander-block.net/eng/Business/eZ-Systems/Personal-PDF-Catalogue

16/05/2010 9:08 am (UTC)   eZ Projects   View entry   Digg!  digg it!   del.icio.us  del.icio.us

ymc

› Frischfilm ist für den Grimme Online Award 2010 nominiert

Frischfilm ist nominiert für den Grimme Online Award 2010. Aus den rund 2000 Einreichungen wurden 23 herausragende Websites in die Endrunde gewählt. Am 30. Juni werden in Köln die acht Gewinner in den Kategorien Information, Wissen und Bildung, Kultur und Unterhaltung sowie Spezial bekannt gegeben. Uwe Kammann, Direktor des Grimme-Instituts, verwies auf die Vielfältigkeit und Vielseitigkeit der diesjährigen Einreichungen bei einer gleichzeitigen Ausdifferenzierung des Angebots sowie dem Gewinn an Professionalität und Reife.

„Frischfilm – Die Kurzfilmplattform des Schweizer Fernsehens“ ist ein Special-Interest-Angebot des Schweizer Fernsehens. Die User können dort unter anderem eigene Kurzfilme veröffentlichen oder auch andere Kurzfilme kommentieren und bewerten. YMC hat die Website in Zusammenarbeit mit Hinderling Volkart (Design) realisiert. Technologische Basis ist ein UGC-Framework (User Genereated Content) auf Basis von eZ Publish in einer Multimandanten-Umgebung.

11/05/2010 5:04 pm (UTC)   YMC   View entry   Digg!  digg it!   del.icio.us  del.icio.us

eZ publish™ copyright © 1999-2005 eZ systems as