Preference layers and portlet modes
Overview of portlet configuration preference layers and portlet modes
Portlets written to comply with the Standard Portlet API access their configuration using the PortletPreferences interface from the standard portlet API. Internally, WebSphere Portal keeps multiple levels of portlet configuration that are aggregated to form the representation of the portlets for the users:
Preferences Portlet mode Description Deployment descriptor Deployed portlet. Represented by a <portlet> tag in portlet.xml. Applies to all occurrences of that portlet on all pages for all users. Administrator config Portlet definition that is a particular copy of a portlet. Applies all occurrences of that portlet definition on all pages for all users. With the administration portlets we can create multiple copies of the same portlet with different configurations on the administrator level. Shared edit_defaults Associated with a particular occurrence of a portlet definition on a page and apply to all users who view the portlet on that page Personalized edit, view, help Single user. Applies only to that particular user who views the portlet on the page.
Access preferences
WebSphere Portal merges the different preference keys and values defined on these levels into a single PortletPreferences object. When the same preference key is defined on multiple levels with different values, the most specific value, takes precedence and values on more general levels are not available. We can set up general default values for preferences that can then be customized by individual users. Preference values can be locked for user customization by declaring them read-only in the deployment descriptor. Read-only values can be modified on the administrator level but can never be overridden in other levels. An example:
- Administrator A configures a database viewer portlet so that it displays the Fish view in the Animals database and shows 10 lines per page. The database setting is locked (read-only) so that users may customize the portlet but cannot use it to view a different database.
- Editor E places the portlet on a page and decides the Mammals view is a more interesting default.
- User U1 personalizes the portlet to display the Birds view with 20 lines per page instead of the defaults that are given by the administrator and the editor.
- User U2 personalizes only the number of displayed lines with a value of 55, so user U2 still sees the Mammals view.
- Other users who view the same page see the Mammals view with 10 lines per page as set by the administrator and the editor.
- Editor E now changes the default view to Reptiles. This does not affect U1, but only U2 and other users that have not personalized that particular preference key.
- Finally, U1 resets the personalization of the database view. As a result, the shared default, which is now Reptiles, is shown again. The number of displayed lines remains at the personalized value of 20.
Modify preferences
A preference is modified using setValue. This normally occurs at the personalized layer and therefore affects only the current user. WebSphere Portal uses two special custom modes from the set of predefined custom modes in the Java Portlet Specification to allow setting up the more general preference levels:
- The edit_defaults custom portlet mode is used to work directly on the shared preferences. In this case, the personalized preferences level is not available.
- Similarly, the config mode is used to read and modify the administrator level of preferences.
- The deployment descriptor level of preferences can change when the portlet is redeployed with a modified portlet.xml. It cannot be modified by portlet code.
Portlet mode Layer to which preference changes are written config Administrator preferences edit_defaults Shared preferences All other modes: edit, view, help Personalized preferences If a portlet wants to use these custom modes, they must be declared with <custom-portlet-mode> tags in the deployment descriptor. Entering one of these modes requires special access permissions. Portlets can enter these modes using the standard APIs for setting a portlet mode. These APIs are ActionResponse.setPortletMode and PortletURL.setPortletMode. However, the portlets do not need to do so, because the portal normally provides buttons in the portlet skin that allow to switch to these modes, if they are supported by the portlet. The administrator level of preferences for a particular portlet can also be modified without actually invoking the portlet itself using the portlet management portlet.
Each call to PortletPreferences.store persists those values at the current preference level that were set using setValue. Even if the value being set is identical to the currently applicable value, which has been set by the more general level, the new value is now stored at the current level and never inherited. That means, to use the preferences hierarchy, so that later changes on a more general level are available to the more specific levels, we must take care to set and store only those preferences that should not be inherited from the more general levels. Resetting of preferences as on the previous example is done programmatically using the reset method. This means the value of the preference key that is reset is now read from the next more general preference layer.
- The reset of a preference is fundamentally different from setValue(null): the latter hides any default value that was set on a more general level.
- If a preference is reset at all levels and has no value defined in portlet.xml, the default value that is passed to the getValue call is returned.
Programming recommendations
The separation of preferences into different layers is mostly transparent to the portlet programmer. In the code, you access the aggregated preferences using the PortletPreferences object. The portal automatically selects the appropriate values for the current user and portlet mode. The only place where the distinction of the preference layers becomes visible in a portlet is in the configuration views. That is in the portlet modes edit, edit_defaults, and config. The different preferences and the different modes in which they are presented determine which part of the portlet behavior can be customized by users and which part can only be customized by editors or administrators.
In practice, it is often difficult for the programmer to anticipate how a portlet is used in a particular environment. In many cases the administrator decides which of the preferences can be customized and at which level they should be customized. Therefore, program the portlets in such a way these decisions are not determined by the code but, as far as possible, by the deployment descriptor. This makes it easier for the administrator later to determine to which extent and on which levels preferences can be customized.
This can often be achieved using the same code (and JSPs) for implementing all three configuration views, edit, edit_defaults, and config, and simply deactivating or hiding those configuration settings that cannot be modified in the current mode. Settings that can be modified by administrators, such asconfig mode, can be determined by the read-only state defined in portlet.xml. There is no standard API used to indicate which preferences are be modified in the shared configuration and which preferences be locked for users. If this is required, we recommend keeping a special (invisible) preference locked-pref-keys that holds a list of those preference keys that can only be modified in edit_defaults or config mode.
Code examples:
- Define the preference names that should be locked for the user in the portlet.xml:
<preference> <name>locked-pref-keys</name> <value>allowed-folders</value> <value>folder</value> </preference>
- Verify the preference is writable before allowing the user to change a preference value:
public boolean isWritable(String prefName) { PortletMode mode = request.getPortletMode(); if (mode.equals(CONFIG_MODE)) return true; PortletPreferences prefs = request.getPreferences(); if (prefs.isReadOnly(prefName)) return false; if (! mode.equals(EDIT_DEFAULTS_MODE)) { String[] lockedKeys = prefs.getValues("locked-pref-keys", null); if (lockedKeys != null && Arrays.asList(lockedKeys).contains (prefName)) return false; } return true; }As previously mentioned, for the normal display of your portlet, we do not need to distinguish whether a particular preference value is an administrator or a user setting. The portal automatically selects the appropriate values for the current user and portlet mode. Nevertheless, consider the merging behavior for the different preference levels particularly in two scenarios:
- When we use multi-valued preferences, for example to store lists, no merging of values takes place; if the preference is personalized, the shared settings are simply hidden.
Example: To implement a scenario where, for example, an administrator defines a part of a bookmark list that all users must see, and users can personalize the list by adding or removing their own entries, use separate keys for administrator and user preferences and merge the values explicitly in the code.
- When values for multiple preference keys depend on each other, we can run into problems if one of the keys is personalized while another is taken from a shared preference level. If the more general level is now changed, the new combination of shared and personalized values may become invalid. In the previous example, the possible values for the database view obviously depend on the selected database. If the administrator levels of preferences are changed from the Fish view of the Animals database to a Plants database and a Trees view, all the specializations for the database view become invalid: preferences for user U2 would still indicate the Reptiles view, but now on a Plants database.
There are multiple options to resolve the second problem; it depends on the logic of the portlet, which of them is most appropriate:
- We can always set all the dependent preference keys in a single operation by setting all of them explicitly before the same call to store.
- We can even merge the dependent preference keys into a single string value. This is what we should do for combined values, for example x-y coordinates. This implies that no merging of preference levels can take place, therefore we cannot use this approach to support a use case scenario in which administrator changes can be partially overridden by users.
- In other cases, it is more appropriate to accept the possibility of invalid preference combinations and provide suitable error handling; for the previous example, the portlet should simply be prepared to handle the fact the specified database view may be invalid and reset that preference key to go back to a - hopefully valid - default that was set on a more general level.
- Another option to resolve this problem is the use of a PreferenceValidator to make sure that only valid preference combinations are available to the portlet.
Preference Validators
The Java Portlet Specification defines how a preference validator is defined in a portlet. If it is present, tools can use it to make sure that only valid preference combinations are saved for a portlet. WebSphere Portal uses a preference validator not only to validate preferences stored, but also to make sure that a change on a more general level has not invalidated the current view. Whenever a portlet retrieves its preferences in a particular context. The available combination of preference levels is validated, if necessary, and preference levels that have become invalid in combination with more general levels are ignored: they are not available to the portlet but are retained in the database. Therefore they can become available again when the conflicting preferences on the more general level are updated to be compatible again. This way we can isolate the validation checking and let the portal take care of providing the display logic with valid data. To verify whether the selective hiding of preference levels works as expected, we can enable traces for the following trace string:
com.ibm.wps.pe.pc.std.core.impl.WPPreferencesHierarchyImpl=low=enabled
This will generate trace logs for validation exceptions that occur during preference reads.
Keep in mind the following restrictions when implementing a preference validator:
- A failed validation on preference access causes the complete conflicting level to be hidden. To be more selective about how preferences are made consistent, use the following approach with self-contained preference validators.
- Preference validators are intended to be used also by stand-alone tools. Therefore they should be self-contained and not require a portal run time environment, network, or back end access. This means that a preference validator would not be suitable for the previous example. That is for checking the specified document really exists in the document database.
Compatibility with portlets that do not support the edit_defaults portlet mode
The Standard Portlet API supports the standard portlet modes edit, view, and help. Generic portlets written to comply with the Standard Portlet API support those portlet modes. But as they are not aware of the preference hierarchy that is implemented in WebSphere Portal, they usually do not support the custom portlet mode edit_defaults. Instead, they only support the standard portlet mode edit for customization. The same applies to portlets that were written to comply with the Standard Portlet API for a previous version of WebSphere Portal, in which the edit_defaults mode was not yet supported. Preference values set in any of the standard portlet modes are never shared between users, in accordance with the behavior assumed by the Java Portlet Specification, because they are always stored at the personalized preference level. Unfortunately, this behavior precludes even the required use case where an Editor wants to set up a read-only page for all users with the appropriate shared portlet configuration. Normally this would be done in the edit_defaults mode, but as the portlet does not support that mode, this is not possible.
To enable editing of shared preferences on such portlets, WebSphere Portal provides a special compatibility mode, the edit_defaults_compatibility portlet mode. Like any other custom portlet mode, this mode must be explicitly enabled in the portlet deployment descriptor, but other than that, the portlet does not require any special coding. To the portlet, the compatibility mode appears as the standard edit mode that it already supports, but the API implementation reads and store the shared preference level instead. As the portlet code does not know about the compatibility mode, entering that mode is only possible from the icon or pull-down menu selection option provided on the portlet title bar. As the compatibility mode is mapped to edit mode for the portlet, a portlet that supports this mode must also support edit mode for the same content types in the portlet deployment descriptor. As an example, consider the following code snippet:
<supports> <mime-type>text/html</mime-type> <portlet-mode>edit</portlet-mode> <portlet-mode>edit_defaults_compatibility</portlet-mode> . . . . . </supports>In the standard portlet modes, such as edit, a portlet that supports the compatibility mode will still read and store personalized preferences only, and changes are not visible to other users. There is, however, a slight difference to the normal aggregation of preference layers: When a user personalizes the portlet, the preferences are stored in a private copy and not on a personalized layer. No merging takes place between shared and personalized preferences, and any subsequent changes on the shared preferences are not visible to a user who has personalized the portlet, no matter which preference keys are affected. This arrangement as been made to avoid issues with inconsistencies between dependent preference values, as portlet code might not be prepared to handle unexpected combinations of preference values resulting from the merging of shared and personalized levels (as described under Access preferences).
As an example, assume the database viewer portlet mentioned under Access preferences was written without support for config and edit_defaults modes, and allows us to customize all settings in the edit mode only. To set up the portlet on a shared page without requiring each user to customize it, its deployment descriptor has been modified to support the edit_defaults_compatibility mode. Editor E sets up the portlet on a shared page using the compatibility mode to display the Mammals view from the Animals database with 10 lines per page. User U personalizes the portlet in edit mode to show 20 lines instead. At this point, a private copy of all preference keys is created, so if editor E changes the shared preferences to display the Reptiles view instead, U will continue to see the Mammals view when viewing the page.
The modes edit_defaults and edit_defaults_compatibility mode are mutually exclusive. A portlet can support only one of them at a time.
Parent Portlet creation basics