Liferay 7.3 Workflow inspection, Part 2

localize your Freemarker templates

Yesterday I wrote about localizing the E-Mail subject. Turns out that identifying the receiver of workflow notifications is tricky.

Another challenge is having your localization utilities available in the Freemarker templates of the workflow nodes.

The only list of variables and objects that developers can use in the templates is from Liferay 6.2 and is probably outdated.

But at least this old list helped me to find out that there is for example a list of ' workflowTaskAssignees '

If you look at the existing notifications you'll notice that these assignees can be roles or a single user. So in 'single user notifications' (like the update task after the Administrators rejection) we should be able to get the users Locale and use it for our purpose.

 <#if (serviceLocator??) && (workflowTaskAssignees?size == 1) && (workflowTaskAssignees[0].getAssigneeClassName() == "com.liferay.portal.kernel.model.User")>
     <#assign
         workflowTaskAssignee = workflowTaskAssignees[0]
         userLocalService = serviceLocator.findService("com.liferay.portal.kernel.service.UserLocalService")
          user = userLocalService.fetchUser(workflowTaskAssignee.getAssigneeClassPK())
          myPorletLanguageService = serviceLocator.findService("path.to.my.portlet.service.MyPortleLanguageService")


This makes some checks (never trust software...), 'finds' the necessary services and enables us to do things like

 param = [userName] <#-- this is the senders username -->
 rejected = myPortletLanguageService.formatLanguage(user.getLocale(), "my-workflow-rejected-x", param)

and later in the template

${rejected!"N/A"}

Ok, that was easy. Now on to the tricky things.

What about a comfortable link in the e-mail that the user can click on and directly access the asset he needs to work on?

Sorry, but I think that's a 'must have'!

For that we need the appropriate URL, that is, as far as I can see, not available in the Freemarker template.

At the end I need something like:

[PortalURL]/group/control_panel/manage/-/my_workflow_tasks/view/[workflowTaskId]

which is the friendly URL of the workflow task. Here's what I've tried and failed:

  • Getting the PortalUrl from the themeDisplay (themeDisplay is not available)
  • Getting the workflowTaskId (nope, not available in the template)

But you can see from the above list of available objects that we have access to 'serviceContext'. And serviceContext holds the PortalURL, but not the workflowTaskId...

But If you read Part 1 of this posting then you know that the workflowTaskId is hidden in the request. So:

 workflowTaskId = paramUtil.getLong(serviceContext.getRequest(), "workflowTaskId")
  portalUrl = serviceContext.getPortalURL()+ "/group/control_panel/manage/-/my_workflow_tasks/view/" + workflowTaskId

does the job. Well... not perfectly because if the user clicks on that link he indeed lands on the workflow asset but, sadly enough, can not edit it.  Because I double checked that the user has the 'UPDATE' permission. (He has it, because the workflow permission logic grants full access to an asset to all users involved in a workflow) I consider this as Bug 3.

Please, If you know how to solve this better, use the comments to help me and others :-)

Coding in the template is not funny (hard to debug, Freemarker is picky about missing variables...) and so I thought I better do this in the WorkflowHandler and stuff the hole URL in the serviceContext.

That was working fine and I simply grabbed it from there with

workflowTaskUrl = getterUtil.getString(serviceContext.getAttribute("workflowTaskUrl"))

in the template.

It works fine ... as long as your pimping the mail of the 'update' task which is an 'onAssignment' notification. But when I tried to use the same technique on the 'review completed' 'onExit' notification I began to scratch my head. But step by step...

Remember that two mails are send to the user upon the rejection of a workflow submission? Exactly the two that I described above. First 'onExit', second 'onAssignment'.

The first is the 'review complete' Mail and first I thought that I probably don't need it. But this notification is also triggered when the submission is finally approved. And as long as you want to inform your user about the approvement I don't see a way to get rid of it.

So at a minimum wanted to pimp this 'review completed' Mail and distinguish between rejection and approvement. Therefore I used the same technique: Stuffing the status 'pending' or 'approved' in the serviceContext in the WorkflowHandler and retrieve it in the Freemarker template.

NOPE! Does not work! Because apparently something messes up the serviceContext in the 'onExit' notifications. BTW: Even the workflowContext, that is available in the action scripts, is messed up in 'onExit'

What you stuff in gets lost. At least partly. I consider this as Bug 4.

I ended up coding things by accessing services directly from the Freemarker template. As a goodie I even accessed the

assetEntryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetEntryLocalService")
 assetEntry = assetEntryLocalService.fetchEntry(entryClassName, entryClassPK?number)

and added the Asset Summery to the mail with

${(assetEntry.getAssetRenderer().getSummary())!"n/a"}

BTW:

${(assetEntry.getAssetRenderer().getURLEdit(serviceContext.getRequest()))!"n/a"}

and using this as the direct link to the asset does not work.

Finally:

The inspection of the Kaleo Worklfow in Liferay 7.3.3 shows a powerful tool that yet has optimization potential. The lack of documentation and some at least strange behavior limits the power regarding the communication in the workflow.

Maybe I'm misled, overlooked something and you can proof me wrong. Your welcome! :-)