Umbraco : Lire les propriétés de documents et de médias à partir de la base de données seule

Ce n’est pas forcément recommandé lorsqu’on utilise un CMS d’exploiter sa base hors de son contexte (web), mais c’est parfois nécessaire. Notamment si l’on veut effectuer des traitements différés, à partir d’un automate ou d’un service Windows par exemple.


C’est exactement ce qu’il m’a fallu faire pour un service d’indexation. Pour la petite histoire, le cas qui m’a intéressé est celui-ci : responsable de plusieurs sites Umbraco hébergés sur un partage réseau (NAS), j’ai mis en place un service Windows pour l’indexation des documents et des médias dans un répertoire Lucene. Lucene n’appréciant guère les partages réseau, sans compter que la mise à jour de l’index ne peut être effectuée que par un thread à la fois, cela rend la mise à jour de cet index plus compliquée depuis le site d’une ferme web. Le service en question devait donc être en mesure de lire en base de données les propriétés des noeuds (de type contenu et média) afin de les indexer.

Version d’Umbraco

Cet article est basé sur Umbraco 4.7. Au moment de la rédaction de cet article, la dernière version est la 4.11.1 (voir le projet sur Codeplex pour connaître la version actuelle).

Bien qu’assez ancienne, il est à noter que le modèle de données d’Umbraco 4.7 n’a que peu évolué. Je pense que les requêtes ci-dessous fonctionneront avec les versions suivantes (peut-être avec de très légères adaptations). Umbraco 4.11.1 se base toujours sur la base de données de la version 4.8.

SQL

C’est en fait relativement simple. Il faut savoir deux choses :

  • La fonctionnalité de rollback d’Umbraco (c’est-à-dire sa capacité de restaurer une ancienne version d’un noeud) s’appuie sur un versioning de chaque propriété de ce noeud. Ceci permet notamment de restaurer une ancienne version d’un noeud même si sa structure (ses propriétés) a changée.
  • Les médias ne sont pas versionnés.

À chaque noeud est associé un historique de propriétés, chacune appartenant à une version particulière. La version est identifiée par un GUID. Les propriétés d’un média ont également une version, mais il n’y en a qu’une seule (pas d’historisation).

Voici un schéma simplifié (Hendy Racher propose également un schéma instructif sur son blog) :

Schema de base de données

SELECT 
 data.contentNodeId, 
 data.versionId, 
 data.propertytypeid, 
 ISNULL
 (
  CAST(dataInt as varchar(max)), 
  ISNULL
  (
   CAST(dataDate as varchar(max)), 
   ISNULL(
    CAST(dataNtext as varchar(max)), 
    CAST([dataNvarchar] as varchar(max)))
  )
 ) as dataString, 
 dataInt, 
 dataDate,
 dataNText, 
 dataNvarchar, 
 t.dataTypeId, 
 t.contentTypeId, 
 t.Alias 
FROM cmsPropertyData data
INNER JOIN cmsPropertyType t ON t.id = data.propertytypeid 
WHERE
 data.contentNodeId = 1056 
 AND data.versionId = 'EF8553C5-59C1-4E6A-A091-D8DEAE85B4CA'

La requête ci-dessus retourne les propriétés du noeud 1056, en version EF8553C5-59C1-4E6A-A091-D8DEAE85B4CA. La colonne dataString contient la valeur de la propriété sous forme de chaîne. Le nom de la propriété est contenu dans la colonne Alias. Dans la base de données, la colonne cmsPropertyData.versionId peut valoir null. À ma connaissance, cela n’est cependant pas possible en pratique et l’on peut considérer que ce cas n’arrivera pas. Je me suis appuyé sur un historique de plus d’un an et toutes les lignes ont une version.

Noter que la nature du noeud n’est pas connue ici. Il peut s’agir d’un document (contenu), d’un média, mais également d’une feuille de style, d’un template, etc..

Dans Umbraco, chaque noeud a une nature identifiée par un GUID. Par exemple :

  • C66BA18E-EAF3-4CFF-8A22-41B16D66A972 : document
  • B796F64C-1F99-4FFB-B886-4BF4BC011A9C : media

La liste complète des types de noeud est dans la documentation officielle.

La requête suivante permet d’obtenir la liste des documents du site (noeuds de contenu) afin de connaître notamment l’identifiant du noeud et sa dernière version:

SELECT
 doc.nodeId, 
 doc.published, 
 doc.documentUser, 
 doc.versionId, 
 doc.text, 
 doc.releaseDate, 
 doc.expireDate, 
 doc.updateDate, 
 doc.templateId, 
 doc.alias, 
 doc.newest 
FROM umbracoNode node 
INNER JOIN cmsDocument doc 
 ON 
  node.trashed = 0 
 AND node.id = doc.nodeId 
 AND doc.newest = 1
 -- AND node.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972'

La table cmsDocument ne contient en fait que les noeuds de contenu, il est donc inutile d’ajouter un critère sur la nature du noeud (umbracoNode.nodeObjectType).

Pour obtenir les médias (et de façon générale tout noeud de nature autre que document), il faut interroger la table umbracoNode de la même façon. La version devra être récupérée avec une requête distincte sur la table cmsContentVersion:


SELECT 
 id, 
 parentID,
 nodeUser, 
 level, 
 path, 
 sortOrder, 
 uniqueID, 
 text, 
 nodeObjectType, 
 createDate
FROM umbracoNode node 
WHERE 
 node.nodeObjectType = 'B796F64C-1F99-4FFB-B886-4BF4BC011A9C'
 AND trashed=0

Pour obtenir la dernière version du noeud 1059:

SELECT TOP 1 VersionId 
FROM cmsContentVersion 
WHERE contentid = 1059 
ORDER BY VersionDate DESC

Les deux précédentes requêtes peuvent évidemment être fusionnées:

SELECT 
 id, 
 parentID,
 nodeUser, 
 level, 
 path, 
 sortOrder, 
 uniqueID, 
 text, 
 nodeObjectType, 
 createDate,
 (
  SELECT TOP 1 VersionId
  FROM cmsContentVersion ver
  WHERE ver.ContentId=node.id
  ORDER BY VersionDate DESC
 ) as VersionId
FROM umbracoNode node 
WHERE 
 node.nodeObjectType='B796F64C-1F99-4FFB-B886-4BF4BC011A9C'
AND trashed=0

Avec ces cinq requêtes, on est en mesure d’obtenir facilement la liste des noeuds du site, quelque soit leur nature, et l’ensemble de leurs propriétés.