Child pages
  • Customising the Grouper UI
Skip to end of metadata
Go to start of metadata

Grouper UI Properties

 These topics are discussed in the "Grouper UI" training series.

As of Grouper 2.2, most of the Grouper UI configuration (for the Admin UI, Web UI's, and new Grouper 2.2 UI) is now in grouper-ui.base.properties, and you can override it in grouper-ui.properties.  See the Configuration Overlay page for details.  Some configuration that you generally will not need to change in upgrading to Grouper 2.2 is in media.properties. 

 This page applies to Grouper versions prior to Grouper 2.2

How to Customize the Grouper User Interface (UI)

   Some settings for Grouper UI customization are described here. Look in the media.properties for more settings in the UI. 

Introduction

This document is written for the web application developer. It describes how to make changes to the Grouper UI and is best understood with reference to the Grouper UI architecture (which contains links to underlying concepts and technologies). The section Determining How a Grouper UI Page Was Constructed explains how to display on screen information, which can help determine what you need to change in order to modify a Grouper UI page. Struts Actions in the Grouper UI gives an overview of which Struts actions relate to which functional areas.

The document focuses on the specific customisations which can be built into the QuickStart demo (see the QuickStart README), and will refer to differences between the standard and custom screen shots in Grouper UIs. You may want to open this link in a separate window / tab for reference.

The UI has been designed so that the source code for the standard UI need not be changed in order to effect customisations. This is intended to make it easier to upgrade changes to the standard UI without compromising customisations you have made.

The structure and contents of grouper-qs/custom-grouper-ui should be used as the starting point for your own custom Grouper UI.

CSS Changes

Grouper has its own CSS stylesheets, but provides a mechanism for site-specific stylesheets to be loaded after the Grouper stylesheets. This allows sites to override/extend existing styles and to add new styles. Such changes can alter the position of screen elements, fonts, colours, etc.

The custom stylesheet was configured in grouper-qs/custom-grouper-ui/resources/custom/media.properties:

    css.additional=custom/custom.css

The actual stylesheet is found at grouper-qs/custom-grouper-ui/webapp/custom/custom.css.

Note: As of version 0.9 of Grouper css.additional can be a space separated list of stylesheets. It is also possible to 'turn off' the Grouper stylesheets by setting the key grouper-css.hide=true.

    Differences in the standard and custom UIs which are due to CSS are listed below:

   Feature

Standard UI

Custom UI

Menu

Positioned vertically on left.

Positioned horizontally below logos. Various colour changes.

Content area

  To the right of the menu.

                      Aligned left and full width.

'Members'

Blue text on pale background.

                  White text on grey background.

Many more CSS classes are applied to elements in the HTML source than are specified in the Grouper stylesheets so a high degree of CSS customisation is possible.

It is possible to turn off the style sheets. This may be useful for testing the accessibility of the Grouper UI and any customisations you make (see the Remove CSS stylesheet references? form field in Determining How a Grouper UI Page Was Constructed.

    In grouper-qs/custom-grouper-ui/resources/custom/media.properties, the following property was set:

            image.organisation-logo=custom/images/banner.logo.gif

Changing the Default Text

In the standard UI, all navigational text and instructions are derived from a Java ResourceBundle based on grouper-qs/grouper-ui/resources/grouper/nav.properties.

In the custom UI, values for keys set in grouper-qs/custom-grouper-ui/resources/custom/nav.properties will override the standard UI values. In the UI example screen shots, the custom UI includes UoB in menu items and changes Help toAssistance.

Through the use of Java ResourceBundles, the Grouper UI supports Internationalization. The default locale for the standard UI is set in grouper-qs/grouper-ui/resources/init.properties:

            default.locale=en_US

In grouper-qs/custom-grouper-ui/resources/init.properties,default.locale=en_GB, however, there is no nav_en_GB.properties file so there are no differences due to locale in the standard and custom UIs.

Currently, there is nowhere in the UI to select a different locale from the default, however, if a lang parameter is passed as part of the URL which invokes login, the value will be used as the locale for the current session.

See Determining How a Grouper UI Page Was Constructed for details of how to display which key / value pairs were used in an actual page in the UI.

If you create your own templates you are not under any obligation to use ResourceBundles instead of directly entering text in templates, however, if you wish to contribute code back to Grouper, such a contribution would be more useful if it used ResourceBundles.

Using Custom Templates Instead of the Standard Templates

The Grouper UI uses Strut's Tiles to define core page components. In the standard UI these are defined in grouper-qs/grouper-ui/webapp/WEB-INF/tiles-def.xml. In the custom UI some definitions are modified and another added in the filegrouper-qs/custom-grouper-ui/webapp/WEB-INF/tiles-def-custom.xml. New definitions for headerDef and footerDef allow site specific branding. groupStuffDef defines a template which is included in the Group Summary page of the UI.

EasyLoginFormDef defines a new page (see Customising Authentication).

Defining Custom Dynamic Templates

Grouper recognises some core entities such as Groups, Stems, Subjects and Collections. The Grouper UI dynamically chooses the appropriate template for an entity at runtime based on its type and the UI context. The default templates for an entity and view are defined in grouper-qs/grouper-ui/resources/grouper/media.properties. When a specific entity-view key is not found, a default is used. Key-value pairs can be overridden, or more specific keys added to grouper-qs/custom-grouper-ui/resources/grouper/media.properties. The algorithms used to choose appropriate keys are described in the Javadoc for DefaultTemplateResolverImpl. This class can be extended to add support for other entity types, or a completely new implementation plugged in (see Javadoc for the TemplateResolver interface.

    In the standard UI the default template for a subject is defined:

                subject.view.default=/WEB-INF/jsp/subjectView.jsp

    In the custom UI:

                personQS.view.default=/WEB-INF/jsp/custom/customPersonSubjectView.jsp

defines a specific template for subjects whose source has an ID of personQS. In the UI examples the name Keith Benson is followed by (kebe) in the custom UI due to the use of /WEB-INF/jsp/custom/customPersonSubjectView.jsp as a template. Subjects and groups may have any number of site specific attributes, so dynamic templates allow sites to create templates which have access to these custom attributes.

See Determining How a Grouper UI Page is Constructed for determining which template was chosen for an entity-view on a page in the UI.

Modifying Existing Structs Actions, Adding new Actions, and Making New Tiles Definitions Available

The Grouper UI is based on Struts and the standard Struts configuration is through grouper-qs/grouper-ui/webapp/WEB-INF/struts-config.xml. Existing actions can be replaced and new ones added to grouper-qs/custom-grouper-ui/webapp/WEB-INF/struts-config-custom.xml.

    See Struts Actions in the Grouper UI for an explanation of how the standard Grouper ui actions interact.

In the custom UI the action /callLogin is redefined such that it forwards to /easyLogin - a new action.

More information on how to change the behaviour of the Grouper Struts actions is available in the appropriate javadoc package description.

Even if you don't need to change/add any actions, a Tiles plugin must be configured in order to make custom templates available (see Using Custom Templates instead of the standard templates):

<plug-in className="org.apache.struts.tiles.TilesPlugin">
  <set-property property="moduleAware" value="true"/>
  <set-property property="definitions-debug" value="0"/>
  <set-property property="definitions-parser-details" value="0"/>
  <set-property property="definitions-parser-validate" value="false"/>
  <set-property property="definitions-config" value="/WEB-INF/tiles-def.xml,/WEB-INF/tiles-def-custom.xml"/>
</plug-in>

Note that the order of files in the definitions-config property is important as the last Tile definition with a particular name loaded is used.

Customising Authentication

The standard UI uses basic HTTP authentication configured through Tomcat and the web application web.xml file. A Filter LoginCheckFilter checks if you are logged in before allowing access to the application. It checks the javax.servlet.http.HttpServletRequest.getRemoteUser(). If not set the user is redirected to the splash page, otherwise, access is granted, and if necessary, the user session initialised.

In the custom UI, when a user clicks the Login link on the splash page, the /callLogin action is requested. This forwards the user to the /easyLogin action which displays the template named EasyLoginFormDef, which is a simple form allowing a username to be entered. The custom UI also defines a Filter EasyLoginFilter which is called prior to LoginCheckFilter. If it sees a request parameter called username, it attempts to load a Subject (through SubjectFinder.findByIdentifier). If successful, it stores the username in the HttpSession and calls the next Filter in sequence with a modified HttpServletRequest, which responds to getRemoteUser() by returning the stored username.

This section shows how new authentication schemes can be introduced. A more serious scheme that allows Yale CAS Authentication has been contributed to the Grouper UI distribution. The introduction of the Lite UI in v1.50 has lead to more configuration elements  to ensure proper authentication. The Cas authentication contribution has been updated accordingly. In addition, Newcastle University have contributed a Wiki page explaining how they integrated the Grouper UIs with Shibboleth.

In order to configure new Filters, it is necessary to modify the web application web.xml file. How to do this is described in Customising web.xml.

Note: The standard UI does not have a logout link, because it is not possible to safely logout of basic HTTP authentication. Other authentication schemes will generally work by setting an HttpSession attribute - which is cleared when an HttpSession is invalidated, so a logout link is provided.

See debug information in logs, edit log4j.properties

log4j.logger.edu.internet2.middleware.grouper.ui.GrouperUiFilter = DEBUG

 

Customising group authorizations

As of v1.3.0 it is possible to override how the UI decides what the current user can do with a group. The primary motivation for this feature is to allow some UI features to be turned off i.e. when the feature should be maintained by a loader. Customization is achieved by implementing the UIGroupPrivilegeResolver interface, and configuring it through media.properties e.g.

edu.internet2.middleware.grouper.ui.UIGroupPrivilegeResolver=uk.ac.bris.is.grouper.ui.UoBUIGroupPrivilegeResolver

Customising the Root Node of the Grouper Repository

    *see also Customizing Browsing and Searching

The Grouper API defines a root stem. In the standard UI the Current location begins with Root whereas in the custom UI it starts with QS University of Bristol. Both screen shots are views of the same Grouper repository. Three scenarios are possible

  1. Root is visible, and browsing starts at Root,
  2. Root is visible but browsing starts at a defined stem e.g. QS University of Bristol.
  3. Root is not visible and browsing starts at a defined stem e.g.QS University of Bristol.

As of Grouper 0.9 it is possible to specify a root node per browse mode (see AbstractRepositoryBrowser). If none is specified, default.browse.stem from media.properties is used. If this is not set the the root node is used and scenario 1 occurs. If the root node is displayed, its name is determined by stem.root.display-name from nav.properties. If this is not set 'Root is used by default.

By default, the hide-pre-root-node value for each RepositoryBrowser defined in media.properties, is set to true. This leads to scenario 3.

If hide-pre-root-node is set to false then scenario 2 occurs.

Creating an InitialStems View

    *see also Customizing Browsing and Searching

This feature is not implemented in either the standard or custom UIs; however, it provides an alternative starting point for browsing, by allowing sites to provide a customised list of stems or quick links. The list of stems can come from any part of the hierarchy, and so may provide a better starting point for users, i.e., for GrouperSystem the default view is:

  • Personal Groups
  • Academic Faculties
  • Student Union
  • Non-Academic Departments
  • Community Groups
  • [All Students]
  • [All Academic Staff]
  • [All Students and Academic Staff]
  • [UoB Administrators]

But for another user (e.g., an art student), the following list might be more appropriate:

  • Personal Groups for <name>
  • Arts Faculty
  • Student Union Clubs

Such a list could be generated in a site-specific way based on a username. A site might also provide a means for a user to edit their list of quick links.

See Javadoc for InitialStems interface.

As of Grouper 0.9 it is possible to define a different InitialStems implementation for each browse mode; see AbstractRepositoryBrowser.getInitialStems()

Customising Browsing and Searching

As of version 0.9 of Grouper it is possible to customise existing browse modes and add new browse modes. It is also possible to specify root nodes and InitialStem implementations on a mode by mode basis.

At runtime RepositoryBrowserFactory is used to obtain a RepositoryBrowser implementation for the current browse mode, by obtaining the class name of the implentation from resources/grouper/media.properties using the key repository.browser.<mode>.class. All Grouper supplied implementations extend AbstractRepositoryBrowser, which reads further properties. Thus, the behaviour of supplied browse modes can be modified by changing relevant properties. The logic can be modified by providing a new implementation class - possibly a subclass of the Grouper implementation.

Alternatively, new browse modes can be implemented and configured. In general you will also need to implement a top level Strut's action and page for any new browse mode, and provide links as appropriate. See Customising the Menu\ for details on how to change the default menu.

Customising the Menu

As of version 0.9 of Grouper the menu is configurable. PrepareMenuActionreads resources/grouper/menu-items.xml to obtain a list of known menu items. A media.properties key, menu.order, determines the order in which items are rendered.

Sites can add additional menu items by creating their own menu-items.xml and adding the file name to the media.properties key: menu.resource.files.

If sites want to have different menus for different users they can subclass PrepareMenuAction and override the protected boolean isValidMenuItem(Map item,GrouperSession grouperSession,HttpServletRequest request) method. You will also need to override the Strut's action prepareMenu.do.

As of version 1.2.1 a new MenuFilter interface has been introduced to allow a more structured approach to customizing menus. Two implementations are provided, configured as:

menu.filters=edu.internet2.middleware.grouper.ui.RootMenuFilter edu.internet2.middleware.grouper.ui.GroupMembershipMenuFilter

GroupMembershipMenuFilter is configured using a UiPermissions object and allows menu items to be vetoed depending on whether or not a user is a member of a group.

Personal Groups

Grouper has no specific support for personal groups, however, by implementing the PersonalStem interface, the Grouper UI will create a 'personal stem' for a user (if one does not exist) at login. An implementation of PersonalStem is provided at grouper-qs/custom-grouper-ui/java/src/edu/internet2/middleware/grouper/customqs/ui/CustomQSPresonalStem.java. This implementation creates a stem (extension=subject id) as a child of /qsuob/personal. Currently any user who is logged in can see personal stems. Whether they can see groups in the personal stem will depend upon Access privileges. Sites could use custom implementations of RepositoryBrowsers to implement their own business rules around personal stems and groups.

Displaying subjects, groups, and stems

Prior to Grouper v1.2.0 it was necessary to use custom dynamic tiles to change how subjects, groups and stems are displayed. This still remains the most flexible approach, especially if you need to show more than one attribute.

As of Grouper v1.2.0 it is possible to configure a single arbitrary attribute to use when displaying a subject, group or stem. The default media.properties keys are:

#Default if an attribute is not configured for a specific subject source. 'description' is set for backwards compatability
subject.display.default=description

#used for subjects which are groups sourced by Grouper
subject.display.g\:gsa=displayExtension

#used for internal subjects i.e. GrouperSystem and GrouperAll
subject.display.g\:isa=name

#default attribute for groups (when not viewed as a subject)
group.display=displayExtension

#flat = context i.e. flat mode in the UI. Here the hierarchy is not shown and names displayExtension need not be unique across
#multiple stems.
group.display.flat=displayName

#default attribute for stems
stem.display=displayExtension

 In the QuickStart the following key is also used:

subject.display.qsuob=name

 When displaying search results sites can configure a default attribute to display:

search.group.result-field=name
search.stem.result-field=name

in addition sites can configure a set of attributes from which the user may select one to display:

search.group.result-field-choice=name displayExtension displayName
search.stem.result-field-choice=name displayExtension displayName

As of Grouper v1.2.1 it is possible, for the SubjectSummary page, to specify a subset of available attributes to display and the order in which to display them:

# subject.attributes.order.<SOURCE_ID>=comma separated list of case sensitive attribute names
subject.attributes.order.g\:gsa=displayExtension,displayName,name,extension,createTime,createSubjectId,createSubjectType,modifySubjectId,modifySubjectType,modifyTime,subjectType,id

#subject.attributes.order.qsuob=LOGINID,LFNAME,subjectType,id

The UI wraps API objects as specific subclasses of ObjectAsMap. As of v1.3.0 it is possible to configure your own implementations. Typically these would subclass the Grouper UI concrete subclasses and add/modify behaviour. Configuration is through media.properties e.g.

objectasmap.StemAsMap.impl=uk.ac.bris.is.grouper.ui.util.UOBStemAsMap

You could use this feature to create virtual attributes as composites of other attributes.

Sort order of lists

As of Grouper v1.2.0 various lists may be sorted alphabetically.

  • search results for:
    • groups
    • stems
    • subjects
  • group memberships
  • group privilegees
  • stem privilegees
  • groups / stems where a subject has a selected privilege
  • saved subjects
  • saved groups
  • stems / groups whilst browsing
  • stems / groups in flat mode

See Javadoc for LowLevelGrouperCapableAction.html.sort and  DefaultComparatorImpl for detailed information.

In principle, the sort algorithm should use exactly what is displayed on screen to sort lists, and, by default, this is what it does. The sort algorithm, therefore, takes account of the configuration for Displaying subjects, groups and stems. On the other hand, Grouper allows the use of dynamic tiles and so it is possible to override the defaults in a way that the sort algorithm cannot work out. If a site does use dynamic tiles to display subjects, groups or stems, it is possible to configure Grouper to use alternate configuration for sorting, but it is the responsibility of the administrator to ensure that the sort configuration is appropriate for what is displayed on screen.  For maximum flexibility, it is also possible to configure different attribute(s) to sort on for different contexts*. The 'search' order for keys is documented for each implementation of GrouperComparatorHelper.

*The different contexts recognised are:

  • search
  • members
  • flat
  • subjectSummary
  • privilegees

As of Grouper v1.2.1 you can sort subjects from the same source together by defining strings which are pre-pended to the usual sort string:

subject.pre-sort.g\:gsa=AAA
subject.pre-sort.qsuob=BBB

Enabling import / export of group memberships

As of Grouper v1.2.0 it is possible to configure the UI to enable import / export of group memberships. Simple implementation classes are provided for dealing with tab or comma delimited files. In general, the formats for import or export vary for different sites. By default import / export is not enabled. Import is controlled by MembershipImportManager and a DefaultMembershipImporter class is provided. Export is controlled by MembershipExporter.

In the QuickStart import and export is 'activated' using:

membership-export.config=resources/custom/membership-export.xml
membership-import.config=resources/custom/membership-import.xml

You can adapt these configuration files for your own needs and even write your own import implementation if the classes provided are unsuitable.

As of Grouper v1.2.1 you can configure the UI to allow import of data from a text area:

membership-import.allow-textarea=true

If a user does not select a file to import, the user is presented with a text area where they can type or paste data.

Customising the Build Process

The Grouper UI uses the grouper-qs/grouper-ui/build.xml ant script to build the web application. This script is configured throughgrouper-qs/grouper-ui/build.properties, which has a key additional.build. In the custom UI this is set to \${basedir}/../custom-grouper-ui/additional-build.xml. It is the responsibility of this script, which is called by the standard script, to compile any Java source files and to copy to the build area any other necessary files. If you wish to incorporate any contributed code, calls to the relevant build scripts should be placed here. In the custom-grouper-ui/additional-build.xml script, the struts-patch build script is called.

Customising web.xml

A web application web.xml file is a key configuration file and any site wishing to customise the Grouper UI will need to modify it.  The web.xml is a J2EE deployment descriptor which configures the Servlets (how URLs are mapped to Java classes), the filters (pre/post logic around servlets), j2ee security (if not done in apache or somewhere else), listeners (for j2ee events), custom tag libraries (how some tags in JSPs map to java classes), etc.  Things you might need to customize are filters (e.g. a new way to do authentication / authorization), security (do you want the servlet container to manage authentication / authorization?), custom tag libraries (are you using a new library in JSP extensions?), etc.

The default deployment descriptor is found at grouper-qs/grouper-ui/webapp/WEB-INF/web.core.xml. The UI provies a mechanism for merging fragments of different web.xml files into a final deployment file. In your additional build script (see Customising the Build Process copy any web.xml fragments into \${temp.dir}. Typically files should be prefixed with a 2 digit number e.g. 20 (90 is used for web.core.ml). The merging process merges in name order of the files.
The custom UI includes two web.xml fragments which, when copied, are prefixed with 00 and 95 so the former is merged with web.core.xml and the latter is merged with the result of the first merge.

The first web.xml fragment is grouper-qs/custom-grouper-ui/webapp/WEB-INF/web.custom.xml and it overrides the default action servlet definition to ensure that it loads the Struts config customisations - which in turn load the Tiles definition customisations.

<servlet>
  <servlet-name>action</servlet-name>
  <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
  <init-param>
    <param-name>config</param-name>
    <param-value>/WEB-INF/struts-config-custom.xml,/WEB-INF/struts-config.xml,/WEB-INF/struts-config-custom.xml</param-value>
  </init-param>
  <init-param>
    <param-name>config/i2mi</param-name>
    <param-value>/WEB-INF/struts-config.xml</param-value>
  </init-param>
  <load-on-startup>2</load-on-startup>
</servlet>

Note: As the order of elements in the final web.xml file is important it can be difficult to get elements in the order you want. The merging process is not extensively tested and it is quite likely it will not work properly for all elements. It may be necessary to rework the merging process, or resort to manual editing of the web.core.xml file.

    The merge process is dependent on web-xml-merge.xsland web-xml-merge-tags.xml found in grouper-qs/grouper-ui.

Running the standard UI at the same time as the custom UI

By applying the struts-patch contribution (see Customising the Build Process), and configuring the Struts action servlet with more than one module see (config/i2mi parameter in Customising web.xml), it is possible to have both the standard and custom UIs available at the same time.

After building the custom Grouper UI you appear to lose the standard UI, however, if instead of accessing /grouper, you access /grouper/i2mi in your web browser, you then get the standard UI.

Determining How a Grouper UI Page Was Constructed

Since a Grouper UI page may be constructed from many templates, including dynamic templates, and it may not be easy to determine which ResourceBundle key was used to render text, a mechanism has been created to display debug information. Go to the URl /grouper/populateDebugPrefs.do. You should see a form:

 

    The form fields are explained in the table below:

Field

Description

Enable debug display

Determines if debug information is shown at the bottom of the page. If not selected, none of the other options are active.

Webapp root for I2mi*

The complete file system path to the standard UI webapp root.

Webapp root for your site*

The complete file system path to the custom UI webapp root.

Show resource keys and values at end of page

If selected, any key-value pairs derived from the nav ResourceBundle to display screen text are listed.

Show resource keys in page rather than values

Instead of seeing the text in the page you will see ?key?

Show dynamic tiles

If selected, a hierarchy of templates used to construct the page is shown. If a template was loaded dynamically, the view, entity type, chosen key and template name are displayed.

Executable for JSP editor

The complete filesystem path to a JSP editor - only use if the server is your working machine!
If the webapp roots above are specified, template names are links which will open the template file in the specified JSP editor.

Remove CSS stylesheet references?

Allows you see the non stylised page - used for checking accessibility in absence of a screen reader.

    *These fields are only required if you wish to link template names to a JSP editor.

Providing Feedback and Getting Help

The Grouper UI is intended to be extensible and not to force unnecessary constraints, however, it is only as sites try to make their own customisations that the true extensibility can be tested. If while customising the Grouper UI you find yourself forced to modify standard Grouper UI sources (of any kind), or find that you cannot easily do what you want to, please offer feedback to, or request help via the grouper-users mailing list.

See Also

Grouper UI Properties

New Grouper 2.2 UI

Grouper UI Building and Installation

  • No labels