Frontend visibility

Define the visibility of views and perspectives based for certain user groups, independent of the action rights this group has.

Introduction

Until now, all views and perspectives of the rich client are secured by action rights, e.g. all views displaying content of an item are secured by the action right "Items, general access". Because this "all or none" approach isn´t really flexible we introduced "Display rights", so every view can be set visible/invisible due to user group(s) the user belongs to. The new display rights are combined with the existing rights, that means a user group still must have the action right AND in addition the display right to see the corresponding view. This document shows the technical background for the new display right, possible customizing approaches and limitations.

Limitations

There is no business logic which handles dependencies between views and perspectives. That means if you disable a perspective for a user group, the views of this perspective can still be openend. In addition it is not possible to restrict the visibility of a view to a defined perspective, so the view can be either shown in all perspectives or none.

There are also no dependencies between the action rights and the display rights. So if you withdraw the action right from a user group (e.g. article), it still has the display rights for the article views and perspectives. Anyway the views and perspectives are not visible for this group anymore, because the user group needs BOTH (action right and display right(s)) to see them.

There are some dependencies between views, that means views try to access other views programatically to get a selection for example. Until now this wasn´t any problem because usually these views had the same action rights and so both were enabled or disabled at the same time. With the display rights this precondition changes and so there are several places where the client could crash. If you find such a place please create a jira issue. Of course avoid any dependencies between views in your custom code! If you don´t know how, google Model View Controller or just ask.

Because of all these missing dependencies the administrators are responsible to configure the frontend reasonable. An import perspective without a repository or data source view does not make sense in any way!

Editors don´t have action rights and so they also don´t have any display rights.

Although most of the checks are working on the fly, it is recommended to restart the client to be sure that the user is not able to open restricted views or perspectives anymore. UI elements like the perspective toolbar can not be updated on the fly while most of the other ui elements can be set invisible immediately.

Repository Changes

We separated the display rights from the action rights and created a new subentity type called "UserGroupAuthorizationType". Now every authorization is represented by one row in the database table with a flag indicating if the right is set or not. Please note that only rights which have been changed are saved, all others are enabled by default. Because of the strict separation to permissions or action rights we use the word "authorization" instead of "right" on technical side. As you can see in the picture below there is also a logical key named "type". In later versions we plan to migrate the action rights to this improved model and via this key we can differentiate between action and display rights then. If you are using the API you don´t have to care about this key, it will be automatically set.

images/download/attachments/84543790/authorization.PNG

images/download/attachments/84543790/authorizationEntity.PNG

New API

As you can see below, a new API class called "DisplayService" is provided to set or get a display right. The DisplayService is accessible via the PresentationComponent class (its the same mechanism as you know already from the EntityManagementComponent).

DisplayService
/**
* The display service handles the authorizations of ui elements. Clients are not supposed to implement this interface.
* The singelton instance can be obtained from the {@link PresentationComponent#getDisplayService()}
* @author sehlers
* @noimplement
* @api
*/
public interface DisplayService
{
/**
* @param loginToken
* @param displayElementId
* @return {@code true} if the user has the permission to see/open the UI element with the given id (e.g. the view or
* perspective id), else {@code false}. Note that this method checks if the user has the general access right
* if there is one (e.g. ArticleRead right for article views) AND the display authorization (actually
* this is a shortcut for {@link #isElementEnabled(LoginToken, String, boolean)}. For
* administrators and system users it will return {@code true} in any case.
*/
boolean isElementEnabled( LoginToken loginToken, String displayElementId ) throws CoreException;
/**
* @param loginToken
* @param displayElementId
* @param checkActionRight if {@code true} it will be also checked if the user has the action right contributed to the
* displayElementId (usually done in a PermissionManager contribution). If there is no action right with the
* displayElementId the method will return {@code false}!
* @return {@code true} if the user has the permission to see/open the UI element with the given id (e.g. the view or
* perspective id), else {@code false}. For administrators and system users it will return {@code true} in any
* case.
*/
boolean isElementEnabled( LoginToken loginToken, String displayElementId, boolean checkActionRight )
throws CoreException;
/**
* @param displayElementId
* @return {@code true} if the UI element with the given id (e.g. the view or perspective id) is hidden, else
* {@code false}. Note that this method doesn't check any user permissions. If a view is "hidden" then it's
* should be hidden for all users (even for administrators).
*/
boolean isElementHidden( String displayElementId ) throws CoreException;
 
 /**
* This method authorizes the given user group to see/open the UI element with the given id (e.g. a view or
* perspective id). Note that this method DOES NOT change the general access right, so if the user group doesn´t have
* the corresponding general access right, the UI element can still NOT be seen/opened
* @param userGroup the user group to authorize
* @param displayElementId the id of the UI element, e.g. the article table view id
* "com.heiler.ppm.article.ui.views.ArticleTableView"
*/
void enableDisplayElement( EntityProxy userGroup, String displayElementId ) throws CoreException;
/**
* This method forbids the given user group to see/open the UI element with the given id (e.g. a view or
* perspective id).
* @param userGroup
* @param displayElementId the id of the UI element, e.g. the article table view id
* "com.heiler.ppm.article.ui.views.ArticleTableView"
*/
void disableDisplayElement( EntityProxy userGroup, String displayElementId ) throws CoreException;
}

Generic View and perspective contributions

We offer an extension point which is described below to contribute new securable display elements. PIM 7.0.02 delivers two contributions for all views and perspectives. That means if you add a view or perspective by yourself, it will be automatically detected at startup and an entry will be shown in the "frontend visibility" view. Anyway you still have to contribute a PermissionProvider to define an action right for the view as in all versions before!

Secure custom UI Elements

By default the DisplayService was introduced to make views and perspectives visible/invisible for different user groups. But you can also use this mechanism to "secure" all other UI elements which you have contributed or implemented. The following example shows how to contribute your UI element at the extension point and check if it should be displayed or not.

images/download/attachments/84543790/pluginExample.PNG

In the example plugin above we contributed a new customized view with a button which should only be displayed if the user has a special right ("com.heiler.ppm.custom.button"). First have a look at the view. We have to contribute a permission provider which adds an action right for the view. In the example we coupled the view to the right "com.heiler.ppm.article.core.permission.ArticleRead" (see code snippet below). This is nothing new because you had to do that in previous versions, too.

public class ActionRightPermissionProvider extends PermissionProviderBaseImpl
{
private static final String PERMISSION_BUNDLE_NAME = "com.heiler.ppm.custom.secured.ui.elements.permission.permissions"; //$NON-NLS-1$
public ActionRightPermissionProvider()
{
addViewPermission( ViewWithSecuredButton.VIEW_ID, "com.heiler.ppm.article.core.permission.ArticleRead" ); //$NON-NLS-1$
}
@Override
protected ResourceBundle getBundle( Locale locale )
{
return ResourceBundle.getBundle( PERMISSION_BUNDLE_NAME, locale );
}
}

The display right comes for free, that means you don´t have to do anything, but when you look at the view "Frontend visibility" you will find your view:

images/download/attachments/84543790/DisplayRightNewView.PNG

In a next step we want to control the visibility of a button within our custom view. Therefore we have to adjust our view as in the example below:

public class ViewWithSecuredButton extends StdViewPart
{
public static final String VIEW_ID = "com.heiler.ppm.custom.secured.ui.elements.view"; //$NON-NLS-1$
public static final String MY_BUTTON_ID = "com.heiler.ppm.custom.button"; //$NON-NLS-1$
@Override
public void createPartControl( Composite parent )
{
Composite composite = createMainComposite( parent );
if ( isDisplayButtonVisible() )
{
Button securedButton = new Button( composite, SWT.PUSH );
securedButton.setLayoutData( new GridData( SWT.CENTER, SWT.CENTER, true, true ) );
securedButton.setText( "I´m the invsible man" ); //$NON-NLS-1$
}
}
 
 private Composite createMainComposite( Composite parent )
{
Composite composite = new Composite( parent, SWT.NONE );
composite.setLayoutData( new GridData( SWT.FILL, SWT.FILL, true, true ) );
composite.setLayout( new GridLayout() );
return composite;
}
 
 private boolean isDisplayButtonVisible()
{
LoginToken loginToken = LoginManager.getInstance()
.getLoginToken();
try
{
return PresentationComponent.getDisplayService()
.isElementEnabled( loginToken, MY_BUTTON_ID, false );
}
catch ( CoreException e )
{
MessageDialog.openError( getSite().getShell(), "Error", "An error occured during access the user rights." ); //$NON-NLS-1$//$NON-NLS-2$
}
return false;
}
 @Override
public void setFocus()
{
// Do nothing
}
}

As you can see we use the DisplayService to retrieve the information if the current user is allowed to see the button. Very important is that "false" will be passed to the isElementEnabled() method, because this means no action right will be checked. If this would be true or not set, HPM will look for an action right with the displayElementId, and because no action right was contributed the method will return false!

Now we have to populate that there is a new securable control, so we create a class called "CustomUIElementProvider" which is shown below. This class is contributed to the extension point "com.heiler.ppm.std.core.displayElementContribution" and simply creates a "DisplayElement" for the button. Note that the id has to be unique. Due to the fact that this is neither a view nor a perspective we defined a group called "Custom Control". You are free to create any group you want, so you can find your display rights easily if you use the grouping function of the table.

public class CustomUIElementProvider implements DisplayElementProvider
{
@Override
public List< DisplayElement > getDisplayElements()
{
List< DisplayElement > displayElements = new ArrayList<>();
displayElements.add( createButtonDisplayElement() );
return displayElements;
}
 
 private DisplayElement createButtonDisplayElement()
{
DisplayElement buttonElement = new DisplayElement( ViewWithSecuredButton.MY_BUTTON_ID );
buttonElement.setDescription( "Right to see the Invisible Man" ); //$NON-NLS-1$
buttonElement.setGroup( "Custom Control" ); //$NON-NLS-1$
buttonElement.setType( "Product Manager Control" ); //$NON-NLS-1$
return buttonElement;
}
}

The last example shows how to secure a menu entry declarative. The final solution is shown in the picture below. To get this you have to do the following steps (if you are not familiar with Commands, Handlers and PropertyTester mechanism just google it or look at the programmer´s guide in your SDK manual):

  1. Create a command and a corresponding handler

  2. Add the command to a menu contribution

  3. Add visibleWhen condition to the menu entry

  4. Add "test" condition and configure the test element as shown in the picture below. As args pass your display right id, as you can see it is the id of our new view in our example. Note that the property and PropertyTester is already provided, so you don´t have to implement something. This mechanism can be used for activeWhen or enableWhen conditions at handlers, too. If you want to check only the display right and NOT the action rights, you can pass "false" as second parameter (note that comma is used as separator). Your "args" would look like "com.heiler.ppm.custom.secured.ui.elements.view, false".

images/download/attachments/84543790/PluginXmlCustom.PNG

Hiding UI elements

This part of DisplayService is available since PIM version 7.1.08 resp. 8.0.03.02

An additional possibility to influence the visibility of UI elements (like views or perspectives) is to “hide” UI elements by using the new service: HiddenElementService. This new service extends the functionality of DisplayService and provides a possibility to hide specific elements independent of user permissions. This may be useful when a view or a perspective can’t work properly due to a specific PIM configuration. For example if a view is contributed for a specific entity which is however deactivated in repository then this view usually can’t work properly. To avoid that such UI elements are available and visible - a new part of frontend visibility was added: “hidden elements”.

The HiddenElementService is integrated in DisplayService. So to determine whether a specific UI element is hidden, you can still use the DisplayService and call the new method: isElementHidden().

To define hidden elements the new HiddenElementProvider can be used. The implementers of this provider can be contributed by using the same extension point as for DisplayElementProvider – “com.heiler.ppm.std.core.displayElementContribution”.

In the standard PIM there is already a contribution of the HiddenElementProviderEntityBasedHiddenElementProvider. This provider checks all view contibutions whether they contain an entity identifier in the “class” attribute, e.g.:

class="com.heiler.ppm.std.core.contribution.ContributionClassFactory:com.heiler.ppm.article.ui.internal.view.ArticleTableView:Article"

In this case the provider checks if the corresponding entity exists in repository and whether this entity is “active”. If the entity is not “active” – the corresponding view will be marked as “hidden”. Hidden views will not be shown in PIM Desktop regardless user permissions. Hidden views are shown neither in the “Open view” dialog nor in the “Interface visibility” view. So they are completely hidden from the user. Even an administrative user is not able to see them.

It is possible to contribute additional custom providers which can define specific UI elements as “hidden”. On the client start the HiddenElementService will be initialized, all registered HiddenElementProviders will be queried and all provided hidden elements will be collected. During runtime of PIM Desktop the “hiding state” of these elements will not be changed.

Conclusion

  • There is a generic approach for views and perspectives, that means you don´t have to do something if you contribute a new perspective or view.

  • Use the DisplayService to set or get a display right.

  • If you want to contribute custom display rights use the extension point "com.heiler.ppm.std.core.displayElementContribution"

  • Use the DisplayElementTester if you want to check the display rights declaratively

  • Never forget that the old action right mechanism is still active and so you have to contribute a PermissionProvider adding view and perspective permissions

  • There is and there will be NO business logic handling dependencies between action and display rights or between perspective and view rights.

You can find the example plugin here:

Example_Plugin_For_Display_Rights.zip