In the past years, I already shared with you - the world - how much I love Nuxeo Studio, our configuration tool which makes it extremely easy to configure applications with business rules, document types, fields, automation scenarios, etc.

When you love something, it’s good to say it out loud (also works with people), so let me say it once again: Nuxeo Studio Rocks! Today I will show yet another example of how cool this tool is: We are going to build a custom thumbnail for a Folderish document…with just Studio, without any Java plug-in!

Yes. This is what we are going to do. Why? Well, because we can do it.

Which leads me to the first song of this Nuxeo Studio Rocks playlist; I Can Do It. It’s a song from the 70s. Simple, basic, joyfull. You can only move your body while listening to it. Which can be a problem while reading this article, so maybe you could first listen to the song, then come back to work. I’ll wait for you right here.

Here is what I want:

  • I have a custom document type, Design.
  • It extends Folder, so it has the Folderish facet, which means it accepts children
  • In its children, it will have Picture and File documents, one of them will have a specific value in its asset:kind field: “For Thumbnail”
  • The business rule is:
    • If a child has this value set, use it for the thumbnail of the Design
    • Else, display the default “Big icon” (as set in the Document Type properties)

So, for example, if I have a Design with two Picture and one File, but none is “For Thumbnail”, then the Design’s thumbnail must be the default one:

No for Thumbnail

On the other hand, if one asset is “For Thumbnail”, then we must build the Design’s thumbnail accordingly:

One for Thumbnail

Let’s build that. The steps are:

  • Contribute the ThumbnailService to tell it how to handle the thumbnails for Design documents.
  • Add the Thumbnail facet to Design.
  • When it is time to build the Design’s thumbnail:
  • Find a child Picture or File whose asset:kind is “For Thumbnail”
  • If we find one, we use its thumbnail (see below, it will be in a different place depending on the type, Picture or File)
  • If not, we just clear the Design’s thumb:thumbnail field (and the Nuxeo Platform will display the “Big Icon” as set in the Document Type definition)

Contribute the ThumbnailService

The ThumbnailService exposes an extension point to handle your custom thumbnails, where basically you say “for every document of a specific type, use this Java class to display the thumbnail, possibly to build it”.

It also exposes default behavior, especially one that is very interesting for us, the ThumbnailDocumentFactory. Basically, the algorithm is: - If the document has the Thumbnail facet and its thumb:thumbnail field is not null, then well, use it. It is the image we want for the thumbnail. - Else (no facet or field is null), just use the “Big Icon” as set in the Document Type definition.

What this means is that this ThumbnailDocumentFactory is perfect and does exactly what we want: For a Design document, it uses it’s thumb:thumbnail field to display the icon (we’ll see below how we fill it). So, we will add an XML extension in Studio, contributing the ThumbnailService, telling it “for any Design document type, please use the ThumbnailDocumentFactory java class”. This is us being lazy: we use what others built, and are thankful to them for what they did for us. <extension target="org.nuxeo.ecm.core.api.thumbnail.ThumbnailService" point="thumbnailFactory"><thumbnailfactory name="thumbnailDesignFactory" doctype="Design" factoryclass="org.nuxeo.ecm.platform.thumbnail.factories.ThumbnailDocumentFactory"></thumbnailfactory></extension> Now, we want to add the Thumbnail facet to our Design.

The Thumbnail Facet

Here, we start with a problem. In my Design document type definition in Studio, I don’t have the “Thumbnail” facet (not that it is written in very, very small font, or in white on white that I don’t see it). It just doesn’t exist in Studio.

But I know this facet exists and is always deployed on the server. It is described here. So, we must tell Studio to display it. Studio not only rocks, but rocks in a very, very smart way: It allows you to use the “Studio Registries” to, basically, declare objects that exist on the server but are not a part of Studio (yet).

So, we now declare our facet in Settings > Registries > Document Facets:

{ "facets":[ { "id": "Thumbnail", "schemas": ["thumbnail"] } ] } We also have to declare the thumbnail schema in Settings > Registries > Document Schemas: { "schemas": { "thumbnail": { @prefix: "thumb", "thumbnail": "blob" } } } Now, we do have our nice, cute, and little box, checked in this screenshot:

Facet is Here

Time for a little interlude and a second song in the Nuxeo Studio Rocks playlist. This is Status Quo - the best rock band ever - opening Live Aid, in 1985.

Build the Design’s Thumbnail

All is in place: Our Design document has the Thumbnail facet, which means it also has the thumbnail schema. Let’s fill it’s thumb:thumbnail field with an image if needed.

We are going to search for any children of current Design whose asset:kind field is “For Thumbnail”. We know we’ll find only Picture or File. We want to duplicate the thumbnail into the thumb:thumbnail field of the Design.

Notice this does not duplicate the binary itself. Thanks to its very powerful binary handler, the Nuxeo Platform makes sure there will never be duplicates of binaries, which is quite cool for disk space.

Here you must know (hence, I’m telling you) that the thumbnail is calculated differently for Picture and File.

  • For File, the Nuxeo Platform uses the thumb:thumbnail field (how convenient)
  • For Picture, the thumbnail is the “Small” picture view (aka “Additional Format” in the UI).

If you are interested in the exact algorithm, you can look at the source code here (for File) and here (for Picture). See the getThumbnail() function for both cases.

All this means than once we get the document whose asset:kind is “For Thumbnail”, we must check its type to get the thumbnail. Which means that JavaScript Automation is perfect as soon as there are conditions (not talking about the fact that I love JavaScript too)

Here you go:

function run(input, params) {

  var docs, design, nxql, blob, asset;

  // Just to make things clear
  design = input;
  // Initialize result (no "For Thumbnail" => defaut "Big Icon")
  blob = null;
  // Query. We want current children, Picture, File, no proxies, no version
  nxql = "SELECT * FROM Document WHERE **ecm:primaryType IN ('Picture', 'File')** AND **asset:kind = 'For Thumbnail'** AND **ecm:path STARTSWITH '" + design.path + "'** AND ecm:isProxy = 0 AND ecm:isVersion = 0";
  docs = Repository.Query(null, {'query': nxql});
  if(docs.length > 0) {
    asset = docs[0];
    // Get the blob from thumbnail, if any
    if(asset.hasFacet("Thumbnail")) {
      blob = asset["thumb:thumbnail"];
    }
    // Picture also has "Thumbnail" facet, but it is empty
    // So we get the "Small" PictureView
    if (blob === null && asset.hasFacet("Picture")) {
      blob = Picture.GetView(asset, {'viewName': "Small"});
    }
  }
  // Assign the blob
  design["thumb:thumbnail"] = blob;
  // Save
  design = Document.Save(design, {});
  // And voilà
  return input;
}

We mixed quite some interesting features here, didn’t we? ThumbnailService, Facets, Studio registry, etc. The results are definitely worth it! Finally, I can sing while heading back home in the sunset:

Yeah I can do it
Yeah I can do it
Yeah-eah-eah-eah-eah!