User-specific versus shared portlet preferences - part 1

This article is the first in a series of 2 posts on how personalization can be achieved in a portal project. Moreover, the concept of sharing personalized settings between users is explained and how this behavior can be changed in Liferay.

See also User-specific versus shared portlet preferences - part 2.
 

Personalization

One of the most important features of a portal is personalization, or the ability for users to change the behavior, content or visualization of portlets. In most projects we've done, customers are very skeptical about personalization. For those customers, enabling personalization equals enabling users to mess things up and call for support afterwards. We believe however that personalization, if implemented the right way, creates a dynamic experience that will increase the usage and ultimately extend the lifetime of the portal.
 

Preferences scopes

The Portlet 2.0 specification (JSR-286) enables personalization through the definition of portlet preferences. According to the spec, preferences are simple key-value pairs that are stored for each portlet and for each user. This means that two users who look at the same portlet will see two different versions of that portlet, according to the preferences they have set on that portlet.

Since Liferay implements JSR-286 you would expect Liferay to store preferences on a per-user basis too. This is however not the case. By default, Liferay stores a portlet's preferences on a per-portlet basis. This means that all users will see the same preferences for a certain portlet instance. Luckily, the scope in which preferences are stored can be changed. For this, we need to update the portlet's liferay-portlet.xml. There are 3 properties that are related to the storage of portlet preferences:

  • preferences-company-wide
  • preferences-unique-per-layout
  • preferences-owned-by-group

The first configuration parameter is the easiest one to understand. If preferences-company-wide is set to true, the preferences for the portlet are stored portal-wide. Every instance of the portlet on every page within every site will have the same preferences. Changing the preferences in one place will change the portlet in all other places. If this parameter is true, the other configuration parameters are ignored.

The other 2 parameters, preferences-unique-per-layout and preferences-owned-by-group, can both be true or false as well. In this case, the combination of these settings will define the scope in which the preferences for a portlet are stored. 

If we do the math, we end up with 5 options for storing portlet preferences. The implications of those options can best be explained by giving an example.
 

Example: QuickLinks portlet

Consider a custom portlet that can be configured to show one or more links to internal or external pages, resources or applications. The set of links that should be visible is configured using portlet preferences. In this case, the preferences scope configuration will decide which quick links will be visible for which users. 

  • Portal-wide quick links (preferences-company-wide=true)
    In this case, all portal users will see the same set of quick links throughout the portal. So even if there are multiple QuickLinks portlets on different pages, they will all show the same links. Usually, you won't allow every user to update company-wide preferences, because users will start to override each other's preferences. It is more common to assign this task to a global administrator.
     
  • Site-wide quick links (preferences-company-wide=false, preferences-unique-per-layout=false, preferences-owned-by-group=true)
    This case behaves very much like the previous one. Except for the fact that the links will be the same in portlets on pages within one site. Each site can thus show its own quick links. Again, it is not recommended to let every site member update these site-wide preferences. The ideal person to manage these kinds of preferences is the site administrator.
     
  • Per-portlet quick links, shared across users (preferences-company-wide=false, preferences-unique-per-layout=true, preferences-owned-by-group=true)
    In this case, every portlet instance has its own preferences. So we can have different QuickLinks portlets in different pages and even in different sites and each of them will show different links. For a given portlet instance however, all users will see the same links. Again it's mostly the site administrator who will manage these links or maybe a small group of users that will manage different parts of a site. Remember that this is the default setting, so it will be used if no configuration is set in liferay-portlet.xml.
     
  • Per-user, per-portlet quick links (preferences-company-wide=false, preferences-unique-per-layout=true, preferences-owned-by-group=false)
    In this case, every user will be able to store a dedicated portlet configuration for each portlet instance separately. So each user can decide for himself which links are visible in the portlet. If there are multiple QuickLinks portlets, the configured links can be different in each portlet. The user must of course be given the appropriate permissions to allow him to update the preferences (enable the PREFERENCES action on the portlet resource).
     
  • Per-user quick links, shared across portlets (preferences-company-wide=false, preferences-unique-per-layout=false, preferences-owned-by-group=false)
    In case there are multiple QuickLinks portlet instances on different pages or even different sites, this setting will share a user's preferences between all those instances. So if a user changes the quick links in one portlet, they will be updated in all other QuickLinks portlets as well. Still, the preferences are not shared across different users.
     

Conclusion

It is clear that Liferay's addition to the Portlet 2.0 specification with regard to portlet preferences enables a great amount of flexibility. Defining the scope in which preferences should be stored is an important decision that needs to be taken during functional analysis, not during development.

One limitation of this is that it seems impossible to store preferences in multiple scopes at the same time. Indeed, the liferay-portlet.xml configuration is applied to all preferences. Sometimes however, it could be useful to store default preferences on a global level (e.g. portal-wide or site-wide), while allowing end-users to override this global configuration with custom preferences. In case of the QuickLinks portlet, an administrator could provide a default set of quick links that are used if a user has not set his own quick links. The next part of this series will explain in detail how this can be achieved in a clean, reusable way.

Blogs
How did you know that I just needed to summarize this?
Less effort for me \o/ - I'll keep this reference in my toolbox.
Very good coverage of portlet preferences.

Ahamed Hasan
Author, Liferay Cookbook
Part 2 of this series was just posted: https://www.liferay.com/web/pmesotten/blog/-/blogs/user-specific-versus-shared-portlet-preferences-part-2.

This part will explain a way for storing preferences for a Liferay portlet in multiple scopes at the same time.
Hello Peter,

First of all, great post. I would really appreciate if you could see my problem I posted on forum and I think this blog post could solve my problem.
Link: https://www.liferay.com/community/forums/-/message_boards/message/47911045

I have used 4th option from your list, "per-user, per-portlet" but it didn't help, two users still see the same data.

Please take a look on a link.

Thank you very much.
Hi Peter, great article!

I need to be able to store preferences by instance, regardless of plid. I was hoping that this could be achieved by setting preferences-owned-by-group=true, preferences-unique-per-layout=false and instanceable=true but the _INSTANCE_xyz is "shaved off" when the preferences are stored. The portlets are embedded dynamically and can possibly be viewed on different layouts. An update to the preferences in one place must be "propagated" so that any other view of the same instance gets the same preferences.

With this requirement, do you think it would be possible to "synchronize" preferences for portlets with the same id (and instance) in a model listener? I assume it's possible to update a model without notifying its listeners or the listener would be called indefinitely?
@Thomas,

Thanks for your feedback!

What I don't get is why you want to use an instanceable portlet. I guess the preferences should be user specific but not really portlet instance specific, so you could use the combination (preferences-company-wide=false, preferences-unique-per-layout=false, preferences-owned-by-group=false). In that case however, your portlet must be non-instanceable. This is no problem as long you don't need multiple portlet instances on the same page.

The approach with the model listener seems rather hacky, so I wouldn't really recommend that.
@Peter

My reasons for wanting preferences by instance are these:

I embed a JournalArticle and optionally a Web Form Portlet in a view. The embedded web form has default preferences set and its instance id is set by a combination of the articleId and the articles groupId. The portlet that shows this view can possibly be added to multiple layouts in multiple sites. If an administrator edits the preferences of the embedded web form, only the form on that particular layout would be affected.

If I could store preferences per instance and group rather than per layout, all views would use the same preferences. Unfortunately, this is not possible as the INSTANCE-part of the portletId is cut off when preferences are stored for preferences-owned-by-group=true and preferences-unique-per-layout=false in combination with instanceable.

I submitted a feature request:
https://issues.liferay.com/browse/LPS-54751

I agree that using a Model Listener is hacky at best and will not go that way. Perhaps I'll look into overriding com.liferay.portal.service.PortletPreferencesLocalService with a hook (or disable to possibility to edit preferences of the web form).

Thanks!
Hello Peter,

I have came across a situation that I need to set a portlet's preferences by group (site), that it should reflect same preferences set-up, while being updated from any page of that group. But, the tricky part is that the portlet should reflect different preferences for public and private pages of same group.

I have already tried with preferences-unique-per-layout and preferences-owned-by-group, but I am observing that on updating portlet's preferences from either public or private pages, it is getting reflect on both.

It there any option to set separate portlet preferences for public and private (pages) of a site?
After using the portletpreference object to store the prefernce ,
So all we need to do is configure the liferay-portlet.xml ?