Some time ago, in the Nuxeo Studio Rocks – Part 0 article, I stated, “I’ll share my experience throughout this series of articles.” Thankfully, I did not make any promise about the frequency. Now, I can make one: The frequency of each release of a "Nuxeo Rocks" episode is based on a super powerful algorithm which inherits from the WhoKnows(1) Design Pattern.

In today's example, we are going to build Queues using Nuxeo Studio. And as the title states the Studio rocks: Let's work with music around. Because the Studio lets you to build about everything you want, let's start with Status Quo, Whatever You Want(2).

“Queue” can have different meanings, depending on the context. For example, it is “where a line of people wait. The line of people is the queue”. That's what Wikipedia says, among other sub-meanings. But the main idea remains. Our context is the following: We want to let the end users being able to easily display a list (a queue) of documents (Nuxeo Documents). Actually, we want to let them display a list of dynamically filtered documents, nicely organized in folders, so instead of looking among billions of documents...

NSR1-01-LotsOfDocs-raw-v3

…they can have nice, clean folders, listing only documents they are working on:

NSR1-02-BienRanges-3

This is why “aka Smart Folder” was added to the title. Our queues are Smart Folders actually. A folder which does not actually contains anything by itself, but dynamically displays a list of elements (here, documents), based on some filtering criteria. We already have this kind of folders everywhere: In our eMail applications, in our OS desktops, … Now, I want them them in Nuxeo.

A Case Management System is a typical example where queues are super useful. An Insurance Claim system for example, where a claim has different states (new claim received, claim opened, fetching information, under evaluation, …), and where different users/groups work on claims depending on their states. For example, users of the “Valuation Specialist” group don't want to handle the fetching of administrative informations, and don't have to archive the claim: They just want to work on the claims ready for evaluation, and inside this kind, they want to work only on accidents, or only on robberies, etc.

To make the example of building queues easier, I'm going to use the default DM template of the Studio. This way, you can follow the steps described here with no hassle: Just run the Studio and use the default “File” document type. No need for specific schemas, specific documents, etc. Well, as you will see in a few moment, you will need to create one new document type to build the queues, but our queues will handle only the native File document type.

For example, you will be using the default Life Cycle and the default Worklow to test your smart folder, changing the state of documents:

NSR1-03-LIfeCycleAndDocWorkflow

Here is what we want. We want to let the user create a new “Queue” folder, and enter filtering criteria for this folder. Say we want to let the user filter the folder's content with:


  • The Lifecycle State

  • And the nature of the document. I'm using this item because it already exists in the Dublincore schema and we also already have a vocabulary to help the user filling the value.


So, for example, a user will be able to create a smart folder listing all approved File reports. That is: All documents whose dc:nature field is “report”, whose lifecycle state is “APPROVED”, and whose type is “File”.

The steps to create such folder are the following. We‘ll be describing them right after this summary:


  1. Create a new document type, which inherits from “Folder” with its specific schema (lifecycle_state, nature) and specific layouts

  2. Create a Content View, whose NXQL filter handles this schema, with dynamic parameters (all the trick is here)

  3. Bind this Content View to the new Document type


That's all.

Easy, right?

Now, let's move to the details.

Starting something new? Let's listen to New Born (Muse).

Create a new document type in the Studio. Say we name it "Queue". It inherits from "Folder"…

NSR1-04-CreateQueueDocument

…and its schema contains two fields:

NSR1-05-CreateQueueSchema

The Create/View/Edit layouts are all the same and stay quite basic here:

NSR1-06-DragDropQueueWidgets

We're almost done (actually, the songs of the playlist are too long. Just wait and get a Coke). Now, we create a Content View. Say we name it “QueueContentView”. It is time set the NXQL filter and the result layouts (the columns to display). The NXQL filter is clearly at the heart of all this Queue/Smart Folder feature. Let's describe what we want as a use case:

“As an end-user I want to display a list of approved Files and report nature”

Translated to Nuxeo query, it looks quite clear: We must query every Document matching the following criteria:


  • The primary type must be “File”

  • The lifecycle state must be “approved”

  • The “nature” field of the dublincore schema must be “report”


Here is the filter in NXQL. Because we are in a Content View, we only have the WHERE clause:







Use Case


NXQL


Primary type must be “File” ecm:primaryType='File'
Lifecycle state must be “approved” AND ecm:currentLifeCycleState = 'approved'
The “nature” field of the dublincore schema must be “report” AND dc:nature = 'report'

Now, we want this query to be dynamic, not hard-coded as it is here. We want to use the values defined in the Queue document, in the lifecycle_state and the nature fields. So, we use the content view parameters feature, and replace the hard-coded values with a question mark. For each question mark, we add a parameter that returns the value of theses fields for the current document, which is, in this context, our Queue document. We are in the context of an EL here and must use:
#{currentDocument.schema.field}

For example:
#{currentDocument.queue.lifecycle_state}.

We also keep the usual filtering which removes the hidden/deleted documents and the proxies. Our final settings are the following:

NSR1-07-NXQL-ContentView

In the “Results” tab, we define the columns to display in this view. We're making it easy here, just the title, the nature, the lifecycle and the last modification date.

NSR1-08-NXQL-ContentView-columns

We're almost done. Just a few seconds. The Final Count Down (Europe)(3) looks good for this final step.

The point is: We have a Queue, Folderish document. But it displays the default Content-View and we want it to display the one we just created. So we open the definition of Queue, go to the "Tabs" tab - which is fun to pronounce, “Content Views” sub-tab and select your new content view:

NSR1-09-SetLayoutInDocumentDef

We are done. Just need to test it at runtime. So let's update the server bound to our Studio project(4), and test:


  • Add some documents of type “File”

  • Select a nature for each of them. Select, for the testing, basic and single-word values such as “Report” or “EMail”. We'll see below why I'm suggesting this.

  • Then for some of these documents, start the default workflow, to change the life cycle state:


NSR1-10-zDocWorkflow

Now, at a workspace level for example, click the “New” button and select “Queue”. Notice how nice is our icon here (this was set in the definition of the document type):

NSR1-11-NewDocument-Dialog-Queue

In the creation layout, enter a value of a lifecycle state and a value for a nature. Be careful here, the NXQL query we used is case sensitive and very strict. You must enter the exact label of a lifecycle state and the exact id of a nature. This is why I suggested you to select a simple nature. For example, the id for “Report” is “report". It is “article” for “Article”.

So, say we want our first smart folder to list all “approved File reports”. Here is how it should be created:

NSR1-12-NewQueue-report-approved-creation

Save.

Enjoy.

All is good.

All is perfect!

It's Time to Dance (The Shoes)!

. . .(dancing). . .

. . .(dancing). . .

(I just can't refrain my body to jump and dance on the table when I hear this one)

That said, and once you have finished dancing, I may need to freeze a bit this happiness. Because yeah, ok, we have our smart folder but do you really find it so user-friendly? Hmmm? Really? Of course it is not: We don't want the end users to know the IDs of the labels! They don't care about IDs. They don't care about the exact character case of the lifecycle state. This is not friendly. No-brainer: The interface must be modified.

This will be done using a vocabulary: The vocabulary for “Nature” already exists, so it's cool. We will create a vocabulary for the lifecycle state. Then, we change the Queue layouts so they use the vocabulary. Let's do it with the “Nature” vocabulary. First we open the Queue's Creation Layout, then the “nature” widget, we change it from “Text” to “Vocabulary”:

NSR1-13-QueueLayout-vocabulary

Now, click the “Select Vocabulary” button and pick-up the good one (“nature”). As it is a built-in vocabulary and it is localized, you should also set the “Localize” property to true. We save, update the server(4)(again) and ta-da!

NSR1-14-Localized-Nature

A step further? With our implementation, both fields, lifecycle_state and nature must hold a value. But maybe the user also wants to display all the Reports, or all the approved files whatever their nature. You may have notice that if you enter only one value (for example, you let the lifecycle state empty), the query finds nothing. This is because the NXQL query uses the very strict, Mister Super Serious "=" sign:

[…] ecm:currentLifeCycleState = ? AND dc:nature = ?

We must change things here. First we replace these “=” with “LIKE”:

[…] ecm:currentLifeCycleState LIKE ? AND dc:nature LIKE ?

Then, we must detect when the user lets a value empty, and if so, replace it with the joker, the “%” sign which tells Nuxeo “All possible values for this field”. So, for each parameter, we use the empty keyword and a ternary conditional expression. Something like:

#{ (empty the_field) ? '%': the_field}

In our case, the expressions are:

#{ (empty currentDocument.queue.lifecycle_state) ? '%': currentDocument.queue.lifecycle_state}

and

#{ (empty currentDocument.queue.nature) ? '%': currentDocument.queue.nature}

Now, we really are done.

We gave smartness to our folders, and they are grateful to us for this.

Waow. This was a super long blog, wasn't it?

(1) Don't Google this pattern. It's my secret. Shared with the NSA but I'm not supposed to know that.
(2) The best band ever. Brit. Never miss them on stage. My playlists will always have at least one track from Status Quo.
(3) Yes. I know, I know. But Anahide (she's in charge of the Studio at Nuxeo) really did insist for me to do a playlist in this series. This is how I take my revenge.
(4) Associating your Nuxeo DM instance to your Nuxeo Studio project