Blogs

A case study of implementing custom User Import module for Liferay

Context 

“Bringing” users from an external Active Directory server or any other directory server supporting LDAP for that matter is a common requirement of almost all portal solutions. That’s the exact reason Liferay has addressed this requirement out of the box (OOB). But in a several cases, business specific rules are involved in user import activity. Which makes it appropriate to go for custom User Import module. 
Liferay exposes a set of API, which is as rich as you’ll need to import users from external directory server. This article is outcome of a successfully implemented User Import module for a client. And contains suggestions which can keep one from falling in to same pitfalls. On the other hand, it is as “high level” as possible so implementers can design as they choose.

Divide and Conquer

Considering Active Directory server as your LDAP source lets break the problem of User Import into parts. 

Reading users from LDAP

Searching directory Server using LDAP

LDAP stands for Light weight Directory Access Protocol. It defines an inter-operable standard API for applications to communicate with Directory Servers. Java’s support and API for LDAP is commonly referred to as Java Naming and Directory Interface (JNDI). See http://java.sun.com/products/jndi/ for more info. 
Consider these points when searching a Directory using JNDI:

  • Consider using connection pool when creating Context
  • Utilize JNDI’s search features. Provide filters and controls to narrow the search result and page the search results. You can learn about Search filters at http://java.sun.com/products/jndi/tutorial/basics/directory/filter.html. Active Directory has support for Paged result set. You can learn about Paged Results at http://java.sun.com/docs/books/tutorial/jndi/newstuff/paged-results.html.
  • Trade-off between page-size of search results. The lesser the page-size the more number of pages and hence more requests to server, but faster response from server. The more the page-size less number of pages and hence less requests to server, but slower response from server. Typically, page size of 100 is used.
  • Keep pool-size and page-size configurable, and same for all other connection parameters like, credentials, Server address etc.

Creating User objects 

Part of the challenge is to create a User object, which is akin to users in Liferay, i.e., users having identical attributes to Liferay’s users. This will take away many complexities. Consider creating an attribute mapper utility for this. And input to this utility should be configurable. At the simplest maintain a comma-separated list somewhere,
ldapattrib1=liferayattrib1,ldapattrib2=liferayattrib2,…..,ldapattribn=liferayattribn
 Got a new attribute? Add to the above list, mapper utility reloads the mapping & you are good to go!
Similarly, if you are planning to update/insert users with roles and organizations membership info, have a mapping for them as well.

Custom Attributes

Consider using Liferay’s Custom User Attributes when corresponding attribute of LDAP is not available in Liferay OOB. Not only they serve the purpose, more of them can be added without a single bit of coding!! Custom attribute is a must know for any Liferay project implementer. Explore Liferay’s “Control Panel > Users > Custom Attributes” section to learn more.

Importing Users to Liferay

Try to abstract away details of implementation that reads users from LDAP. Import user should ideally be only concerned about adding/updating users to Liferay. It is not in interest of import service to know where users are coming from. This way you can easily reuse the same code for some other source of users, e.g. a database.
Liferay exposes a set API for working with Users. The exposed API is mostly consistent between local version and remote version (which is exposed as Web Service). 

Important Liferay APIs

  • UserSerivceSoap: Allows you to perform operations such as add, update, activate, deactivate Users

  • GroupServiceSoap: Important service for getting organizational group for users

  • RoleSerivceSoap: Important for getting organizational or group roles for users

  • UserGroupRoleServiceSoap: If you want to assign a user an organization role, this is the service for you

  • ExpandoValueServiceSoap: Use this service to add value of a custom attribute

 
To learn more about Groups, Roles, Organization see Liferay Admin Guide at http://www.liferay.com/documentation/5.2
If you are interested in what all WebServices are exposed, open this URL of your Liferay server: http://localhost:8080/tunnel-web/secure/axis/ 

Retrieving SOAP service instances

Handle to Liferay exposed WebSservice is retrieved through Locators implemented by Liferay. An example to get GroupServiceSoap will be: 

new GroupServiceSoapServiceLocator().getPortal_GroupService("http://10144:test@localhost:8090/tunnel-web/secure/axis/Portal_GroupService");
Similarly you can user service specific locator and get server instances of all the services listed in above section. Also notice that, Liferay API works on user Id (10144, in above e.g.) rather than user name.

Consider Update implications

It is very likely that after User Import module is done importing users, it will be required to update user information of existing users if LDAP server has some updated information for that user. This can lead to unwanted surprises. Take an example of a User, which was imported by the custom import module. Now, administrator assigns some roles to this user manually through Liferay Control Panel. Next time the module runs it overwrites this information with information coming from LDAP!! To handle this, clearly specify and conclude which attributes are additive in nature and which are overwritten. Attributes such as Role membership, Organization membership, Organization Role membership can be additive. To implement additive updates, pre-read the user’s attribute values from Liferay and create a composite set of values that contain both LDAP and Liferay values for that attribute. Call the update service with this composite set of values for that attribute. 

Scheduling

Even though not a must, scheduling can take away pains of an administrator by automatically triggering import jobs at desired time. Open source scheduling frameworks like Quartz are enterprise ready. Quartz also recognizes Unix cron job syntax. To learn Quartz visit http://www.quartz-scheduler.org/docs/tutorial/.

Summary

User Import is one module that has high chances of getting reused in your organization. So it should be kept as configurable as possible and as modularized as possible. Also, consider issues like time required to complete a run of module, because this is the period when Directory Server and Liferay will be out of sync.
 
Advait Trivedi,
Prinicipal Consultant,
CIGNEX India Office

CSS hacks for supporting multiple web browsers

 
This article is for developers who are more touch with HTML and CSS and generally working with table less layout. Due to the nature of the browser many developers faces problems to design the HTML and CSS. For example suppose you fixed some problems in browser like Firefox then same layout break in Internet Explorer, suppose you fixed in IE7 and it breaks in IE6. It takes lot of time to fix the problems for web developers. Its really a nightmare for developers to get the pixel perfect layout.
 
Sometimes you need to use some special exception rules to fix the layout for the special browser and there is no other way. There are lots of CSS hacks are available that will make sure that certain CSS styles are or are not passed by certain browser.
 
Mainly there are two ways to hack the CSS, Conditional comments and Easy selectors.
 
Conditional comments
 
There are again two types of Conditional comments, Positive and Negative.
 
The syntax for conditional comments is as follows:
 
Positive
<!--[if condition]> HTML Content <![endif]-->
Negative
<!--[if !condition]> HTML Content <![endif]-->
 
And condition is one of the following: 

  • IE
    • Any version of IE
  • lt IE version
    • Versions less than version
  • lte IE version
    • Versions less than or equal to version
  • IE version
    • Only version version
  • gte IE version
    • Versions greater than or equal to version
  • gt IE version
    • Versions greater than version

Where version is the version number of Internet Explorer browser, 5, 5.5, 6, 7 or 8.
 
In the above example, styles.css applies to all browsers, ieonly.css only applies to all versions of Internet Explorer, ie6_and_below.css applies to all versions of Internet Explorer below IE 7 and not_ie.css applies to all non-IE browsers.
 
Easy selectors
 
Most in-CSS hacks deal with selector bugs. Below is a list of different IE versions and the beginnings of selectors that are known to select elements in them. All of these selectors use valid CSS.
 
IE 6 and below

  • * html {}
  • IE 7 and below
    • *:first-child+html {} * html {}
  • IE 7 only
    • *:first-child+html {}
  • IE 7 and modern browsers only
    • html>body {}
  • Modern browsers only (not IE 7)
    • html>/**/body {}
  • Recent Opera versions 9 and below
    • html:first-child {}

Note that the hack for IE 7 and below is actually two separate selectors: one for IE 7 and one for IE 6 and below. The rest of the desired selector must be added to both parts of the hack. The two parts cannot be combined with a comma, because IE 6 and below will fail to correctly parse the selector and won't be targeted.
 
Good to know
 
!important
 
Cascading Style Sheets cascade. This means that the styles are applied in order as they are read by the browser. The first style is applied and then the second and so on. What this means is that if a style appears at the top of a style sheet and then is changed lower down in the document, the second instance of that style will be the one applied, not the first. For example, in the following style sheet, the paragraph text will be black, even though the first style property applied is red:
 
p { color: #ff0000; }
 
p { color: #000000; }
 
The !important rule is a way to make your CSS cascade but also have the rules you feel are most crucial always be applied. A rule that has the !important property will always be applied no matter where that rule appears in the CSS document. So if you wanted to make sure that a property always applied, you would add the !important property to the tag. So, to make the paragraph text always red, in the above example, you would write:
 
p { color: #ff0000 !important; }
 
p { color: #000000; }
 
Conclusion
 
Now a days, most of the recent browsers have very good support for CSS - certainly good enough for you to be using CSS to control layout and presentation. Sometimes however, certain page elements will appear differently in different browsers. Don't worry too much if you don't know the reason why - if you can fix it up with these CSS hacks then your web pages should look great across all browsers!
 
By
Vijay Thummar
Consultant at CIGNEX, India Office
 

Liferay PDF Viewer Portlet using ICEFaces & ICEPdf frameworks

We at CIGNEX developed online PDF Viewer Portlet using ICEFaces and ICEPdf frameworks. It is based on ICEPDF reference implementation.  This PDF Viewer portlet provides following features.

  • It is integrated with liferay's document library. So if we add this portlet on any community/organization page. It allows us to browse pdf documents available in document library.

 

  • It allows us to Zoom in & out pdf pages. We can also rotate the pages if we want.
  • It also allows us to view the portlet in full screen mode.
  • It provides quick links to recently browsed documents by the user.
  • It allows us to directly jump on bookmarked page if pdf document contains any bookmarks.
  • We can configure maximum size of recently accessed files through preferences mode.
  • We can easily configure the portlet to display documents from server file-system instead of document library. We just need to set the root folder path through preferences mode.

By
Samir Bhatt
Principal Consultant at CIGNEX, India Office