Locale Specific Friendly URL

In Liferay, for locale specific friendly URLs, first of all it is checked whether there is duplicate locale for same language id or not. Like for languageid as “en” there are many locale like “en_GB”, “en_US”, and for languageId having no duplicate locale, URL contains only languageId like “ja”, “ru”, “de”. So this the case with Japan, Russia and others. There is no duplicate locale for these language so in URL you are seeing like /ja/ or /ru/ and so on.

Below is the code of portalImpl.java where duplicate locale is checked, see the highlighted and bold code below

protected String buildI18NPath(Locale locale) {
            String languageId = LocaleUtil.toLanguageId(locale);

            if (Validator.isNull(languageId)) {
                  return null;
            }

            if (LanguageUtil.isDuplicateLanguageCode(locale.getLanguage())) {
                Locale priorityLocale = LanguageUtil.getLocale(
                     locale.getLanguage());

                if (locale.equals(priorityLocale)) {
                     languageId = locale.getLanguage();
                }
           }
            else {
                  languageId = locale.getLanguage();
            }

            return StringPool.SLASH.concat(languageId);
      }

To override above issue, and make consistent friendly url for all locale, do below,
override “locales” property in portal-ext.properties and add “ja” before “ja_JP” and “ru” before “ru_RU” in that property value.

Browse HSQL DB In Liferay

Sometimes due to restrictive environment or any other reason we could not use any other database like mysql or oracle and we have use default built in HSQL DB in liferay.

Now if we want to browse DB table or table data, following are the steps to do so,

  1. Download SquirrelSQL.
  2. Goto command prompt and type java -jar squirrel-sql-<version>-install.jar. This will install query browser
  3. Open the installed SquirrelSQL. Open “drivers” pane and double click on “HSQLDB In-Memory”. In the opened window, click the “extra class path” tab. Click “add” and navigate to hsql.jar from <LIFERAY_HOME>\tomcat-7.0.27\lib\ext. The “class name” field below should now be filled in with “org.hsqldb.jdbcDriver”.
    driver
  4. Now create an alias. Click new (the + icon). The URL should be of the form jdbc:hsqldb:file:<path>/lportal

For example: jdbc:hsqldb:file:<LIFERAY_HOME>data/hsql/lportal

alias

Now connect the database and you will see all the table under PUBLIC as belowtables

Install SSL Certificate to Tomcat

Generate Private Key (Keystore)

First step is to create private key which details about your domain and organization

  1. Open command prompt
  2. Issue below command

keytool -genkey -alias server -keyalg RSA -keysize 2048 -keystore  priteshblog_com.jks -storepass changeit

You are prompted to provide and confirm the information the certificate contains.

  1. First and Last Name: Enter the fully qualified domain name (FQDN) of the server. For example, blog.pritesh.com
  2. Organizational Unit: The name of your department within the larger organization. For example, PriteshBlog
  3. Organization: The name of your organization. For example, PriteshBlog
  4. City or Locality: The city where your organization is based. For example, Ahmedabad
  5. State or Province: The state of province where your organization is based. For example, Gujarat
  6. Two-letter Country Code: The country where your organization is based. For example, IN for India or US for the United States of America.
  7. Key Password for Alias: Here we have provided “server” as alias of the domain keystore. Provide the password and remember it. For example, I have provide password as “changeit”

cm

 With above, priteshblog_com.jks is the created in your current folder, which is your private key

Generate CSR (Certificate Signing Request)

The next step is to create a certificate signing request (CSR) file which you will submit to a certificate authority. This proves the identity of the server you are asking them to validate.

Depending on the certificate authority you are working with, you will provide the CSR file either by uploading it via their customer service portal or emailing it to your sales representative. For any question contact their technical support or sales teams.

Now issue below command to your command prompt to generate CSR

keytool -certreq -alias server -file priteshblog_com.csr –key store priteshblog_com.jks -storepass changeit

With this, priteshblog_com.csr is been created in your current directory.

Here we are going to see Free SSL Trial for 90 days from InstantSSL (Comodo)

Get Free SSL Trial Certificates from Comodo from here: http://bit.ly/168O8lV

You will receive an email from Comodo with 5 files (crt files).

Once you receive crt files from Comodo, import them into your private key/keystore (remember the one created in the step 1 (priteshblog_com.jks)

Go to the folder where your private key/keystore (priteshblog_com.jks) is stored.

Issue below commands to import your crt files into keystore/private key

  • Import Root CA certificate
    • keytool -import -trustcacerts -alias root -file priteshblog_com/AddTrustExternalCARoot.crt -keystore priteshblog_com.jks

Here “priteshblog_com/” is the folder where your all crt files from Comodo located

You will get message ““Certificate was added to keystore

  • Import Intermediate certificate
    • keytool -import -trustcacerts -alias intermediate_filename -file priteshblog_com /UTNAddTrustSGCCA.crt -keystore priteshblog_com.jks
    •  keytool -import -trustcacerts -alias intermediate_filename1 -file priteshblog_com /ComodoUTNSGCCA.crt -keystore priteshblog_com.jks
    •  keytool -import -trustcacerts -alias intermediate_filename2 -file priteshblog_com /EssentialSSLCA_2.crt -keystore priteshblog_com.jks

Depending on the type of certificate that was purchased, there may be more than one Intermediate certificate in the chain of trust. Please install all intermediates in numberical order until you get to the domain/end entity certificate. Refer: http://bit.ly/1aJRwBY

You will get message ““Certificate was added to keystore

  • Import Entity/Domain certificate
    • keytool -import -trustcacerts -alias server -file priteshblog_com / priteshblog_com.crt -keystore priteshblog_com.jks

See the alias “server” it should be same as you have provide while creating keystore/private key

You should receive message “Certificate reply was installed in keystore” after importing domain/entity ceritificate. P.S message should not be “Certificate was added to keystore

Important Note: Do see you are importing all the crt files from authority providers into the private key/keystore you have created in step 1

Now configure SSL in tomcat,

  1. Place your keystore say (priteshblog_com.jks) to <<tomcat_home>>/conf/keystore folder
  2. Open server.xml from <<tomcat_home>>/conf/
  3. Locate “<Connector port=”8443″ ” and uncomment it and add keystoreFile and keystorePass or paste below code,

<Connector port=”8443″ maxThreads=”200″ scheme=”https” secure=”true” SSLEnabled=”true”                    keystoreFile=”conf/keystore/priteshblog_com.jks” keystorePass=”changeit” clientAuth=”false” sslProtocol=”TLS”/>

  1. Comment <Listener className=”org.apache.catalina.core.AprLifecycleListener” SSLEngine=”on” /> in server.xml. Refer: http://bit.ly/151O1Yl

Start your tomcat and try to access with URL https://blog.pritesh.com:8443/

http

Javascript function in loop passing dynamic variable value

Many of the times we need to attach event iterating over loop of components but we need to pass some dynamic value which should be available when actually listener is called.

For example, we are iterating over list of buttons and attaching event listener of click event but want to pass current index when click event is actually called

We try something like this,


var buttons = $('input[type=button]');

for (var i = 0, n = buttons.length; i<n; i++) {
 var button = buttons[i];
 button.addEventListener('click', function() {
       buttonClick(i, button); // i, el will have last updated values
 });
}

function doSomethingWith(i, button){
 console.log("i: " +i + " " + "button: "+button);
}

Above code won’t work because  the function is actually created once (instead of each iteration of the loop) and that one function points to the last known values of the variables it uses.

Each callback expects one handler instance or function. Now lets create function and return function with proper variables so with each iteration function returns brand new function with proper value defined. So each callback have different instance of function value with appropriate value

Below is the sample


(function(variable) {
 return function() {
 // do something with variable
 }
})(value);

Let us examine above sample

There are two function: an outer and an inner. The inner function that contains your original code. The outer function is wrapped in parenthesis and passes some “value”. This means that the inner function can use the identifier “variable” and it will refer to whatever value was passed to it. So with each iteration, outer function is called with value passed in and returns instance of inner function as handler.

Lets use this in above sample,


$(document).ready(function(){
 var buttons = $('input[type=button]');
 for (var i = 0, n = buttons.length; i<n; i++) {
   var button = buttons[i];
   button.addEventListener('click', (function(i, button) {
     return function() {
       buttonClick(i, button);
     }
   })(i, button));
 }
});
function buttonClick(i, button){
 console.log("i: " +i + " " + "button: "+button);
}

Test the sample and see with each click to button, you will get different value for i and el

Hope this helps!!!

Search in Image using Tesseract in Alfresco

Tesseract in an open-source OCR tool. It extracts text from images into txt files. Please find Tesseract at Tesseract

Many of the times we get requirement to search text inside image in Alfresco. We can do this using Tesseract.

We’ll integrate Tesseract as a transformer in Alfresco. This means that you can upload an image of some text (such as a scan of a form or letter) to Alfresco Share site document library, and runtime transformer configured will transform image into plain text using Tesseract. Alfresco is wired such a way that when it transform document (such as our image) into plain text (using our transformer) then it will automatically index the document using the result of the transformation. So this enables you to search for images in a Share document library based on text located in those images.

Alfresco provides content transformation without writing any code, just by doing some configuration in Spring to execute runtime command for transformation.

Now create tesseract-ocr-transform-context.xml for windows as below, place this xml file at shared/classes/alfresco/extension/


<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans default-lazy-init="false" default-autowire="no" default-dependency-check="none">
 <bean id="transformer.worker.ocr.tiff" class="org.alfresco.repo.content.transform.RuntimeExecutableContentTransformerWorker" lazy-init="default" autowire="default" dependency-check="default">
 <property name="mimetypeService">
 <ref bean="mimetypeService" />
 </property>
 <property name="checkCommand">
 <bean class="org.alfresco.util.exec.RuntimeExec" lazy-init="default" autowire="default" dependency-check="default">
 <property name="commandsAndArguments">
 <map>
 <entry key="Windows.*">
 <list>
 <value>C:\Windows\System32\cmd.exe</value>
 <value>/C</value>
 <value>dir E:\SA\Alfresco\ocr.bat</value>
 </list>
 </entry>
 </map>
 </property>
 <property name="errorCodes">
 <value>1</value>
 </property>
 </bean>
 </property>
 <property name="transformCommand">
 <bean class="org.alfresco.util.exec.RuntimeExec" lazy-init="default" autowire="default" dependency-check="default">
 <property name="commandsAndArguments">
 <map>
 <entry key="Windows.*">
 <list>
 <value>C:\Windows\System32\cmd.exe</value>
 <value>/C</value>
 <value>E:\SA\Alfresco\ocr.bat</value>
 <value>"${source}"</value>
 <value>"${target}"</value>
 </list>
 </entry>
 </map>
 </property>
 <property name="errorCodes">
 <value>1,2</value>
 </property>
 </bean>
 </property>
 <property name="explicitTransformations">
 <list>
 <bean class="org.alfresco.repo.content.transform.ExplictTransformationDetails" lazy-init="default" autowire="default" dependency-check="default">
 <property name="sourceMimetype">
 <value>image/tiff</value>
 </property>
 <property name="targetMimetype">
 <value>text/plain</value>
 </property>
 </bean>
 <bean class="org.alfresco.repo.content.transform.ExplictTransformationDetails" lazy-init="default" autowire="default" dependency-check="default">
 <property name="sourceMimetype">
 <value>image/png</value>
 </property>
 <property name="targetMimetype">
 <value>text/plain</value>
 </property>
 </bean>
 </list>
 </property>
 </bean>
 <bean id="transformer.ocr.tiff" class="org.alfresco.repo.content.transform.ProxyContentTransformer" parent="baseContentTransformer" lazy-init="default" autowire="default" dependency-check="default">
 <property name="worker">
 <ref bean="transformer.worker.ocr.tiff" />
 </property>
 </bean>
</beans>

Now create bat file to execute Tesseract command for converting image into plain text and copy target to Alfresco/temp directory at C:\Alfresco\


REM to see what happens
mkdir c:\tmp
echo from %1 to %2 >>C:\tmp\ocrtransform.log

copy /Y %1 "C:\TMP\%~n1%~x1"
echo target %~d2%~p2%~n2
REM call tesseract and redirect output to $TARGET
"C:\Program Files (x86)\Tesseract-OCR\tesseract.exe" "C:\tmp\%~n1%~x1" "%~d2%~p2%~n2" -l eng

Now restart/start your alfresco server.

Goto <your-site>/Document Library/. Upload on image with .tiff or .png extension and search for some word inside that image from global search text box in Alfresco Share.

Tesseract is great OCR tool, but it’s still far away from having perfect accuracy.

Performing OCR on a long document can be a time consuming and expensive process. Though Alfresco transformation engine is quite good at queuing and prioritising transformations to avoid affect to user experience too much, but if you are uploading a lot of images on modest hardware, there will be substantial drop in performance.

Liferay Theme: CSS Limits in IE

IE has CSS limitation, got know this when tested pages in IE, and found many of the style are not applied and pages looked weird in IE, whereas it was working perfectly fine in other browsers.
Due to more than 50 imports and huge stylesheets, in custom.css, final main.css created in .sass-cache was more than 350KB

Below are the IE CSS limitations per stylesheet,
The rules are:

  • A sheet may contain up to 4095 rules
  • A sheet may @import up to 31 sheets
  • @import nesting supports up to 4 levels deep
  • CSS file size upto 288 KB

To overcome the size issue, divide your styles into multiple CSS files.
keep styles in custom.css with a benchmark of 288KB and then created another CSS file and included that portal_normal.vm

Add below code to include another CSS file in head,
init_custom.vm

#set ($custom_main_css_file = $portalUtil.getStaticResourceURL($request, "$css_folder/custom-main.css"))

Add below code in portal_normal.vm under <head> tag below $theme.include($top_head_include)

#css ($custom_main_css_file)

Above steps includes another CSS files in head section after main.css.

Do keep your css styles which overriding liferay default theme styles in custom-main.css as its going to be loaded after main.css.
Do keep this IE limitation in mind while implementing rich UX having huge number of styles

Run javascript function as admin privileges

In Alfresco, create group, user, add user to group, set permission are allowed only to Admin group users.
Many times we get a requirement where we need to create group and users dynamically on some action.
Now javascript API are available to create group but works only for Admin group users.

I had a requirement to create groups dynamically when rule is executed. But rule was executed action taken by non-admin user.
So I created custom javascript API to run entire javascript function as Admin privileges.
For this you have create one Java class which extends BaseScopableProcessorExtension. BaseScopableProcessorExtension class is extended whenever you want to create custom javascript API.

Register your javascript API in *-context.xml

Then use your API in your javascript.

Below is my java code for custom javascript API to run function with Admin privileges
public class RunAsAdminUtil extends BaseScopableProcessorExtension {
 /** *
 * This method runs javascript function with Admin privileges
 * @param func
 */
 public void runAsAdmin(final Function func) {
 final Context cx = Context.getCurrentContext();
 final Scriptable scope = getScope();
 RunAsWork raw = new RunAsWork() {
 public Object doWork() throws Exception {
 func.call(cx, scope, scope, new Object[] {});
 return null;
 }
 };
 AuthenticationUtil.runAs(raw, AuthenticationUtil.getSystemUserName());
 }
}
Below is the *-contex.xml entry,
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
 <bean id="runAsAdminUtil" parent="baseJavaScriptExtension" class="com.priteshblog.repo.jscript.RunAsAdminUtil">
 <property name="extensionName">
 <value>runAsAdminUtil</value>
 </property>
 </bean>
</beans>

Below is the code of javascript where you would be using custom created javascript api to run function with admin privileges


function main() {
 if (!people.isAdmin(person))
 { //if current logged in user is not admin run function with admin privileges
 runAsAdminUtil.runAsAdmin(createGroupsDynamically);
 }else{
 createFolderStructureFromTemplate();
 }
}
var createGroupsDynamically = function createGroupsDynamically() {
 var name = document.properties.name;
 var TEST_READ = name + '_read';
 var TEST_WRITE = name + '_write';
 people.createGroup(TEST_READ);
 people.createGroup(TEST_WRITE);
}
main();

Create Rule in Alfresco using Custom Javascript API

Create rules dynamically in script. You can create rule programmatically using JAVA API but there is nothing available in javascript API from Alfresco to create rule on the fly.
Take a scenario where you want to add rule on space when its created using script. I couldn’t find any way of doing this so I tried creating a custom javascript API for it.
All work was done and tested on Alfresco 4.0 CE and higher till 4.2.c

Getting Started
It leads to an idea of adding rule description in JSON format in a script then passing it to a Java class, then in java class we can parse JSON and get the rule description to create rule on the fly using JAVA API

Create JSON format for rule in this page as a starting point:
You have to modify it based on what the various actions and condition handlers expect for parameter names

Create a class which would read the rule configuration from JSON objects passed from script. Name this class as CustomRuleCreator and extends it with CreateRuleWizard mostly to use its condition and action handlers.

The constructor takes a couple of helper classes. They important thing here is the call to super.init(). This initializes the maps of condition handlers and action handlers with key on “conditionName” and “actionName”.

public CustomRuleCreator(ServiceRegistry serviceRegistry, Repository repository) {
 this.serviceRegistry = serviceRegistry;
 this.repository = repository;
 super.init(null);
 }

The createRule() method sets some of the simple rule properties such as title, description, etc in the parent class, calls setupRule() to set the more complex rule properties, then attaches the rule to the space.


public void createRule(NodeRef targetSpace, JSONObject ruleDetail)throws JSONException {
 this.ruleJson = ruleJson;
 this.setTitle(this.ruleJson.getString("title"));
 this.setDescription(this.ruleJson.getString("description"));
 this.setType(this.ruleJson.getString("ruleType"));
 this.setRunInBackground(this.ruleJson.getBoolean("executeAsynchronously"));
 this.setApplyToSubSpaces(this.ruleJson.getBoolean("applyToChildren"));
 this.setRuleDisabled(this.ruleJson.getBoolean("disabled"));

 // create the new rule
 Rule rule = new Rule();
 rule.setRuleType(this.getType());

 // setup the rule
 this.setupRule(rule);

 // save the rule
 this.getRuleService().saveRule(targetSpace, rule);
 }

The setupRule() and createCondition() methods are overrided from CreateRuleWizard and modified to read detail from JSON objects.
Create a method  say convertJsonToMap() which converts JSON Objects into map. It parses JSON and checks if scripts or destination parameters are passed in rule details then checks it values are noderef or relative paths. Also it converts any “true” or “false” values to a Boolean object and so on for Integer, Long and Double.

Now make this api available to script as javascript api. For this create a class name it as CustomRuleScript and extends org.alfresco.repo.jscript.BaseScopableProcessorExtension.


public void createRule(ScriptNode targetSpace, String ruleJson) throws JSONException {
 JSONObject json = new JSONObject(ruleJson);

 CustomRuleCreator customRuleCreator = new CustomRuleCreator(this.serviceRegistry, this.repository);
 customRuleCreator.createRule(targetSpace.getNodeRef(), json);
 }

Above method takes targetSpace as parameter, which as space on which we want to add rule on the fly.
We need to add this entry in *-context.xml file


<bean id="customRuleScript" class="com.priteshblog.repo.rule.jscript.CustomRuleScript" parent="baseJavaScriptExtension">
 <property name="extensionName">
 <value>customRuleScript</value>
 </property>
 <property name="serviceRegistry">
 <ref bean="ServiceRegistry"></ref>
 </property>
 <property name="repository" ref="repositoryHelper">
 </property>
 </bean>


Below is the script to create rule on space,


function main(){
 var guestHome = companyhome.childByNamePath("Guest Home");
 copyDocumentRule(companyhome, guestHome.nodeRef);
 }

 function copyDocumentRule(ruleOnNode, destinationNode){
 var rule = '';
 rule += '{';
 rule += ' "title": "Copy Document",';
 rule += ' "description": "Copy Document to folder",';
 rule += ' "ruleType": "inbound",';
 rule += ' "applyToChildren": "false",';
 rule += ' "executeAsynchronously": "true",';
 rule += ' "disabled": "false",';
 rule += ' "action" : {';
 rule += ' "actionName": "composite-action",';
 rule += ' "actions": [';
 rule += ' {';
 rule += ' "actionName": "copy",';
 rule += ' "parameterValues": {';
 rule += ' "destinationLocation": "'+destinationNode+'"';
 rule += ' }';
 rule += ' }';
 rule += ' ],';
 rule += ' "conditions": [';
 rule += ' {';
 rule += ' "conditionName": "no-condition",'
 rule += ' "notcondition" : "false"';
 rule += ' }';
 rule += ' ]';
 rule += ' }';
 rule += '}';
 ruleUtil.createRule(ruleOnNode, rule);
 }
 main();

Create rule-script.js with above script code and upload it to Data Dictionary/Scripts folder
To test above script, goto view details in company home and go to run action. Select execute script and then script above script and click on finish. You will see new rule created on company by checking “Manage Content Rule” on space.

You will have to do some debugging and check what are parameters name and action name and so on. You can check this by creating rule manually from “Manage Content Rule” and use webscripts to check rule detals. For webscripts, check this link
CustomRuleScript.java


package com.priteshblog.repo.rule.jscript;
 import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
 import org.alfresco.repo.jscript.ScriptNode;
 import org.alfresco.repo.model.Repository;
 import org.alfresco.service.ServiceRegistry;
 import org.json.JSONException;
 import org.json.JSONObject;

 public class CustomRuleScript extends BaseScopableProcessorExtension {

 protected ServiceRegistry serviceRegistry = null;

 protected Repository repository = null;

 public CustomRuleScript() {
 }

 /**
 * Creates a rule (described by the given JSON) on the given space.
 * Assumes the current user has permission to create rules on the space.
 *
 * @param targetSpace the space to create the rule on
 * @param ruleJson a JSON string describing the rule
 * @throws JSONException if error parsing JSON
 */
 public void createRule(ScriptNode targetSpace, String ruleJson) throws JSONException {
 JSONObject json = new JSONObject(ruleJson);

 CustomRuleCreator customRuleCreator = new CustomRuleCreator(this.serviceRegistry, this.repository);
 customRuleCreator.createRule(targetSpace.getNodeRef(), json);
 }

 public ServiceRegistry getServiceRegistry() {
 return serviceRegistry;
 }

 public void setServiceRegistry(ServiceRegistry serviceRegistry) {
 this.serviceRegistry = serviceRegistry;
 }

 public Repository getRepository() {
 return repository;
 }

 public void setRepository(Repository repository) {
 this.repository = repository;
 }
 }

I have added condition for custom rule actions. For rule actions it will need handlers. For e.g for copy rule action, there is a CopyHandler. For custom rule action, where you have only created action-executor and not handler below code will work. Check else in setUpRule method which allow to execute further if handler for custom action not found

CustomRuleCreator.java


package com.priteshblog.repo.rule.jscript;
 import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;

 import org.alfresco.model.ContentModel;
 import org.alfresco.repo.model.Repository;
 import org.alfresco.service.ServiceRegistry;
 import org.alfresco.service.cmr.action.Action;
 import org.alfresco.service.cmr.action.ActionCondition;
 import org.alfresco.service.cmr.action.CompositeAction;
 import org.alfresco.service.cmr.action.CompositeActionCondition;
 import org.alfresco.service.cmr.repository.NodeRef;
 import org.alfresco.service.cmr.repository.NodeService;
 import org.alfresco.service.cmr.rule.Rule;
 import org.alfresco.web.bean.actions.IHandler;
 import org.alfresco.web.bean.rules.CreateRuleWizard;
 import org.alfresco.web.bean.rules.handlers.BaseConditionHandler;
 import org.alfresco.web.bean.rules.handlers.CompositeConditionHandler;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;

 public class CustomRuleCreator extends CreateRuleWizard {

 private static final long serialVersionUID = 1L;

 private static final Log logger = LogFactory.getLog(CustomRuleCreator.class);

 /** the value of this parameter must be a node ref as a String */
 protected static final String PROP_SCRIPT = "script";

 /** the value of this parameter must be a node ref as a NodeRef */
 protected static final String PROP_DESTINATION = "destinationLocation";

 protected JSONObject ruleJson = null;

 protected ServiceRegistry serviceRegistry = null;

 protected Repository repository = null;

 public CustomRuleCreator(ServiceRegistry serviceRegistry, Repository repository) {
 this.serviceRegistry = serviceRegistry;
 this.repository = repository;

 super.init(null);
 }

 /**
 * Creates a rule on the given space using rule properties provided via JSON in the constructor.
 *
 * @param targetSpace
 * the space the rule will apply to
 * @param ruleJson
 * a JSON object describing the rule to create
 * @throws JSONException
 * if error parsing JSON string
 */
 public void createRule(NodeRef targetSpace, JSONObject ruleJson)
 throws JSONException {
 if (logger.isDebugEnabled()) {
 logger.debug("creating rule");
 }
 this.ruleJson = ruleJson;

 this.setTitle(this.ruleJson.getString("title"));
 this.setDescription(this.ruleJson.getString("description"));
 this.setType(this.ruleJson.getString("ruleType"));
 this.setRunInBackground(this.ruleJson.getBoolean("executeAsynchronously"));
 this.setApplyToSubSpaces(this.ruleJson.getBoolean("applyToChildren"));
 this.setRuleDisabled(this.ruleJson.getBoolean("disabled"));

 // create the new rule
 Rule rule = new Rule();
 rule.setRuleType(this.getType());

 // setup the rule
 this.setupRule(rule);

 // save the rule
 this.getRuleService().saveRule(targetSpace, rule);
 }

 /**
 * Sets up the given rule using rule properties provided via JSON in the constructor.
 *
 * @param rule
 * the rule to setup
 * @throws JSONException
 * if error parsing JSON
 */
 protected void setupRule(Rule rule)
 throws JSONException {

 // setup the rule
 rule.setTitle(this.title);
 rule.setDescription(this.description);
 rule.applyToChildren(this.applyToSubSpaces);
 rule.setExecuteAsynchronously(this.runInBackground);
 rule.setRuleDisabled(this.ruleDisabled);

 JSONObject ruleAction = this.ruleJson.getJSONObject("action");

 // assume composite action
 CompositeAction compositeAction = this.getActionService().createCompositeAction();
 rule.setAction(compositeAction);

 JSONArray conditions = ruleAction.getJSONArray("conditions");
 for (int cIdx = 0; cIdx < conditions.length(); cIdx++) {
 JSONObject jsonCond = conditions.getJSONObject(cIdx);

 ActionCondition condition = createCondition(jsonCond);

 if (condition instanceof CompositeActionCondition) {
 CompositeActionCondition compositeCondition = (CompositeActionCondition) condition;

 compositeCondition.setORCondition(jsonCond.getBoolean(CompositeConditionHandler.PROP_CONDITION_OR));
 compositeCondition.setInvertCondition(jsonCond.getBoolean(CompositeConditionHandler.PROP_CONDITION_NOT));

 JSONArray subconditions = jsonCond.getJSONArray("conditions");

 for (int sIdx = 0; sIdx < subconditions.length(); sIdx++) {
 JSONObject jsonSubCond = subconditions.getJSONObject(sIdx);

 compositeCondition.addActionCondition(createCondition(jsonSubCond));
 }
 }
 compositeAction.addActionCondition(condition);
 }

 // add all the actions to the rule
 JSONArray actions = ruleAction.getJSONArray("actions");
 for (int aIdx = 0; aIdx < actions.length(); aIdx++) {
 JSONObject jsonAction = actions.getJSONObject(aIdx);

 String actionName = jsonAction.getString(PROP_ACTION_NAME);
 this.action = actionName;

 JSONObject jsonParams = jsonAction.getJSONObject("parameterValues");
 Map actionProps = this.convertJsonToMap(jsonParams);

 // get the action handler to prepare for the save
 Map repoActionParams = new HashMap();
 IHandler handler = this.actionHandlers.get(this.action);
 if (handler != null) {
 handler.prepareForSave(actionProps, repoActionParams);
 }else{
 repoActionParams =actionProps;
 }

 // add the action to the rule
 Action action = this.getActionService().createAction(actionName);
 action.setParameterValues(repoActionParams);
 compositeAction.addAction(action);
 }
 }

 /**
 * Creates an action condition based on a JSON object
 *
 * @param jsonCond
 * JSON object containing condition properties
 * @return an action condition
 * @throws JSONException
 * if error parsing JSON object
 */
 private ActionCondition createCondition(JSONObject jsonCond)
 throws JSONException {
 String conditionName = jsonCond.getString(PROP_CONDITION_NAME);

 Map repoCondParams = new HashMap();

 Map conditionProps = this.convertJsonToMap(jsonCond);

 // get the condition handler to prepare for the save
 IHandler handler = this.conditionHandlers.get(conditionName);
 if (handler != null) {
 handler.prepareForSave(conditionProps, repoCondParams);
 }

 // add the condition to the rule
 ActionCondition condition = this.getActionService().createActionCondition(conditionName);
 condition.setParameterValues(repoCondParams);

 // specify whether the condition result should be inverted
 Boolean not = (Boolean) conditionProps.get(BaseConditionHandler.PROP_CONDITION_NOT);
 if (not == null) {
 not = Boolean.TRUE;
 }
 condition.setInvertCondition(((Boolean) not).booleanValue());
 return condition;
 }

 /**
 * Converts a JSON object to a Map
 *
 * @param jsonObj
 * JSON object to convert
 * @return a Map where key is a String and value is a Serializable object
 * @throws JSONException
 * if error parsing JSON object
 */
 @SuppressWarnings("rawtypes")
 protected Map convertJsonToMap(JSONObject jsonObj)
 throws JSONException {

 // convert JSON to Map
 Map propMap = new HashMap();
 Iterator keyIt = jsonObj.keys();
 while (keyIt.hasNext()) {
 String key = (String) keyIt.next();
 String value = jsonObj.getString(key);
 Serializable propValue = null;

 // convert repository paths to node references
 if (PROP_SCRIPT.equals(key) || PROP_DESTINATION.equals(key)) {

 NodeService nodeService = this.serviceRegistry.getNodeService();
 NodeRef nodeRef = null;
 String storeProtocol = null;
 String storeIdentifier = null;
 String nodeUUID = null;

 // is value a node reference?
 if (NodeRef.isNodeRef(value)) {
 nodeRef = new NodeRef(value);

 storeProtocol = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_STORE_PROTOCOL);
 storeIdentifier = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_STORE_IDENTIFIER);
 nodeUUID = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_UUID);
 }
 else {
 // assume we have a repository path relative to Company Home
 // search for the path within Alfresco content repository
 String nodePath = "workspace/SpacesStore/" + value;
 nodeRef = this.repository.findNodeRef("path", nodePath.split("/"));
 if (nodeRef == null) {
 throw new JSONException("Path " + nodePath + " not found.");

 }
 else {
 storeProtocol = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_STORE_PROTOCOL);
 storeIdentifier = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_STORE_IDENTIFIER);
 nodeUUID = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_UUID);
 value = storeProtocol.concat("://").concat(storeIdentifier).concat("/").concat(nodeUUID);
 }
 }
 if (PROP_SCRIPT.equals(key)) {
 propValue = value;
 }
 else if (PROP_DESTINATION.equals(key)) {
 propValue = nodeRef;
 }
 }
 if (propValue == null) {
 if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
 propValue = jsonObj.getBoolean(key);
 }
 }
 if (propValue == null) {
 try {
 propValue = jsonObj.getInt(key);
 }
 catch (JSONException jsone) {
 logger.info(jsone.getMessage());
 }
 }
 if (propValue == null) {
 try {
 propValue = jsonObj.getDouble(key);
 }
 catch (JSONException jsone) {
 logger.info(jsone.getMessage());
 }
 }
 if (propValue == null) {
 try {
 propValue = jsonObj.getLong(key);
 }
 catch (JSONException jsone) {
 logger.info(jsone.getMessage());
 }
 }
 if (propValue == null) {
 // default to String
 propValue = value;
 }
 propMap.put(key, propValue);
 }
 return propMap;
 }
 }

Reference: site

Find Journal Article (Web Content) By Category

Hello All,
I had a requirement to create Categories, attach those categories to web contents, browse the categories and then display web content based on the category selection. So I need to get Journal Articles based on selected CategoryIds.
Below is the code to find Journal Article by CategoryId

public List<JournalArticle> findArticleByCategory(final long groupId, final long[] categoryIds)
{
	AssetEntryQuery assetEntryQuery = new AssetEntryQuery();
	assetEntryQuery.setAnyCategoryIds(categoryIds);
	assetEntryQuery.setGroupIds(new long[] { groupId });
	assetEntryQuery.setClassNameIds(new long[] { PortalUtil.getClassNameId(JournalArticle.class.getName()) });
	List<AssetEntry> entries = null;
	JournalArticleResource articleResource = null;
	JournalArticle journalArticle = null;
	List<JournalArticle> lArticles=new ArrayList<JournalArticle>();
	try
	{
		entries = AssetEntryLocalServiceUtil.getEntries(assetEntryQuery);
		for (AssetEntry entry : entries)
		{
			articleResource = JournalArticleResourceLocalServiceUtil.getJournalArticleResource(entry.getClassPK());
			lArticles.add(JournalArticleLocalServiceUtil.getArticle(groupId, articleResource.getArticleId()));
		}
	} catch (SystemException e1)
	{
		e1.printStackTrace();
	} catch (PortalException e)
	{
		e.printStackTrace();
	}
	return lArticles;
}

Hope this might help to someone.
Happy Coding!!!!