In my last Blog entry (First Day at the SPA) I showed how to create a simple Newsletter Signup Form with the JSON API and some AngularJs, and earned a relaxing day at the Spa, but now what?
I still want more, more relaxing days at the pool, good and healthy food, muscle relaxing shiatsu massages, and still be able the deliver robust solutions.
So how can you continue optimizing my workflow, and still have time to chill?
The short Answer
JSON Webservices and a little bit of HTML and Javascript, and XML. (And Yes we are still on a Vanilla Liferay Installation)
The long Answer
To show my take on the long answer, I will continue optimizing the small example, of my last Blog entry.
The example Task: Adapt and Optimize the Mailing List sign up (control), to reflect a more realistic Signup form.

So the basic appearance won't change, only the signup process in the background.
Lets first look at the current process
Step 1) You enter an email-address.
Step 2) Your email-address is saved.

Now lets look at the "improved" process
Step 1) The user enters an email-address.
Step 2) An automatic email is send to the entered email-address
Step 3) The user has to activate the subscription, with the provided link in the email
Step 4) After activation (clicking the link), the email-address is marked as activated

Like this achiving a "double opt in" for the Mailing List, which can save you much problems, at least in austria.
So Lets begin
I'am still using the prior coded liferay-service-provider.js, which was updated since the last post. (It can be found on Github)
Step 1: Create our “database”
Step 2: Adapat the HTML/Javascript "CODING"
<div class="name-box"><style type="text/css">.name-box .span4.no-margin{margin:0;}</style><div class="span4 no-margin" data-ng-app="newApp"><div class="ng-view"></div><script src="/angular/angular.min.js"></script><script src="/angular/angular-route.min.js"></script><script src="/angular-addon/liferay-service-provider.js"></script><script>(function(app){app.config(["liferayProvider","$routeProvider", function (liferayProvider,$routeProvider) {liferayProvider.setToken(btoa("newsletterservice:newsletterservice"));liferayProvider.addTypes({name:"Newsletter",type: TYPES.DDLRECORD,groupId: 24913,recordSetId: 25719});$routeProvider.when("/", {template: "<form class='form-search' id='nameBox'>" +"<div class='input-append'>" +"<input class='search-query' data-ng-model='Email' placeholder='Enter Name ... ' type='text' />" +"<button class='btn' data-ng-click='saveName()' type='button'><i class='icon-envelope'> </i>Add Name</button>" +"</div></form>",controller: "baseController"}).when("/success", {template: "<div class='alert alert-success'><b>Success!</b> Your name is now entered in our mailingslist!</div>"}).when("/failure", {template: "<div class='alert alert-error '><b>Error!</b> Please try it again <a href='#/' > go back </a>!</div>"}).when("/activate/:id",{template: "<div class='alert alert-info'>{{message}}</div>",controller: "activationController"}).otherwise("/");}]).controller("activationController", ["$scope", "$routeParams", "liferay", function($scope, $routeParams, liferay){liferay["Newsletter"].update({Activated:true}, $routeParams.id).then(function(){$scope.message = "Email activated!";}).catch(function(){$scope.message = "Error!";});}]).controller("baseController", ["$scope", "$location", "liferay", function($scope, $location, liferay){$scope.saveName = function(){liferay["Newsletter"].create({Email:$scope.Email}).then(function(){$location.url("/success");}).catch(function(){$location.url("/failure");});};}]);}(angular.module("newApp",["LiferayService", "ngRoute"])));</script></div></div><div class="name-box"><style type="text/css">.name-box .span4.no-margin{margin:0;}</style><div class="span4 no-margin" data-ng-app="newApp"><div class="ng-view"></div><script src="/angular/angular.min.js"></script><script src="/angular/angular-route.min.js"></script><script src="/angular-addon/liferay-service-provider.js"></script><script>(function(app){app.config(["liferayProvider","$routeProvider", function (liferayProvider,$routeProvider) {liferayProvider.setToken(btoa("newsletterservice:newsletterservice"));liferayProvider.addTypes({name:"Newsletter",type: TYPES.DDLRECORD,groupId: 24913,recordSetId: 25719});$routeProvider.when("/", {template: "<form class='form-search' id='nameBox'>" +"<div class='input-append'>" +"<input class='search-query' data-ng-model='Email' placeholder='Enter Name ... ' type='text' />" +"<button class='btn' data-ng-click='saveName()' type='button'><i class='icon-envelope'> </i>Add Name</button>" +"</div></form>",controller: "baseController"}).when("/success", {template: "<div class='alert alert-success'><b>Success!</b> Your name is now entered in our mailingslist!</div>"}).when("/failure", {template: "<div class='alert alert-error '><b>Error!</b> Please try it again <a href='#/' > go back </a>!</div>"}).when("/activate/:id",{template: "<div class='alert alert-info'>{{message}}</div>",controller: "activationController"}).otherwise("/");}]).controller("activationController", ["$scope", "$routeParams", "liferay", function($scope, $routeParams, liferay){liferay["Newsletter"].update({Activated:true}, $routeParams.id).then(function(){$scope.message = "Email activated!";}).catch(function(){$scope.message = "Error!";});}]).controller("baseController", ["$scope", "$location", "liferay", function($scope, $location, liferay){$scope.saveName = function(){liferay["Newsletter"].create({Email:$scope.Email}).then(function(){$location.url("/success");}).catch(function(){$location.url("/failure");});};}]);}(angular.module("newApp",["LiferayService", "ngRoute"])));</script></div></div>
Step 3: Create a workflow XML file that reflects the needed/wanted process/flow
I must say the Liferay Workflowengine is pretty neat, and has some nice features. In my opinion it could improve documentation wise, but I cant be that bad, if I could wipout this xml in a few hours.
<?xml version="1.0"?> <workflow-definition xmlns="urn:liferay.com:liferay-workflow_6.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:liferay.com:liferay-workflow_6.2.0 http://www.liferay.com/dtd/liferay-workflow-definition_6_2_0.xsd"> <name>Newsletter process</name> <description>Newsletter</description> <version>1</version> <state> <name>created</name> <metadata></metadata> <actions> <action> <name>start</name> <script><![CDATA[ var _log = Packages.com.liferay.portal.kernel.log.LogFactoryUtil.getLog("WORKFLOW"); _log.error("START"); var email = ""; var id = 1; try{ var idOffset = 3; id = workflowContext.get("entryClassPK") - idOffset; var record = Packages.com.liferay.portlet.dynamicdatalists.service.DDLRecordLocalServiceUtil.getRecord(id); email = record.getFieldValue("Email"); } catch(e) { _log.error(e); } Packages.com.liferay.util.mail.MailEngine.send( Packages.javax.mail.internet.InternetAddress("test@liferay.com"), Packages.javax.mail.internet.InternetAddress(email), "Activation Email", "<html><body><a href='http://liferay:8080/web/spa-tests/main-page#/activate/" + id + "'>activation</a></body></html>", true );]]></script><script-language>javascript</script-language><execution-type>onEntry</execution-type></action></actions><initial>true</initial><transitions><transition><name>timer</name><target>timer</target></transition></transitions></state><task><name>timer</name><assignments><user/></assignments><task-timers><task-timer><name>Timer</name><description>1</description><delay><duration>1</duration><scale>minute</scale></delay><recurrence><duration>1.0</duration><scale>minute</scale></recurrence><timer-actions><timer-action><name>approve</name><script><![CDATA[var idOffset = 3;var id = workflowContext.get("entryClassPK") - idOffset;var record = Packages.com.liferay.portlet.dynamicdatalists.service.DDLRecordLocalServiceUtil.getRecord(id);if(record.getFieldValue("Activated") == true){var companyId = workflowContext.get("companyId");var workflowInstanceLink = Packages.com.liferay.portal.service.WorkflowInstanceLinkLocalServiceUtil.getWorkflowInstanceLink(companyId,workflowContext.get("groupId"),workflowContext.get("entryClassName"),workflowContext.get("entryClassPK"));var workflowInstanceId = workflowInstanceLink.getWorkflowInstanceId();Packages.com.liferay.portal.kernel.workflow.WorkflowInstanceManagerUtil.signalWorkflowInstance(companyId,workflowContext.get("userId"),workflowInstanceId,"end",workflowContext);}]]></script><script-language>javascript</script-language><priority>1</priority></timer-action></timer-actions></task-timer></task-timers><transitions><transition><name>end</name><target>end</target><default>true</default></transition></transitions></task><state><name>end</name><actions><action><name>Approve</name><description>Approve</description><script><![CDATA[Packages.com.liferay.portal.kernel.workflow.WorkflowStatusManagerUtil.updateStatus( Packages.com.liferay.portal.kernel.workflow.WorkflowConstants.toStatus("approved"), workflowContext );]]></script><script-language>javascript</script-language><execution-type>onEntry</execution-type></action></actions></state></workflow-definition>
1) On entering the intial State, an email is sent, with the activiation link
2) Now the Engine is checking periodically (1 minute interval), if the email-address was activated, through the link
3) If the entry was activated, the workflow will terminate. (this is a great place to an some code, that sends a welcome email, or so)
Now, just upload the workflow

and finally add it to the prior created DDL List

Last Step
Lean back and enjoy the time you have saved, and will save in the future, if you take advantage of the rich toolset that Liferay provides out-of-the-box.
Please share any Ideas, possible Improvements, Comments, Feedback and/or nice Spa locations in/near austria. I would like to hear from you.


