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 theFolderish
facet, which means it accepts children - In its children, it will have
Picture
andFile
documents, one of them will have a specific value in itsasset: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:
On the other hand, if one asset is “For Thumbnail”, then we must build the Design
’s thumbnail accordingly:
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 toDesign
. - When it is time to build the
Design
’s thumbnail: - Find a child
Picture
orFile
whoseasset: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
orFile
) - 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:
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 thethumb: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!