Grouper UI Properties


Some information on this page applies to Grouper versions prior to Grouper 2.2 and represents an outdated approach. 
For some UI customizations, the Custom UI feature of Grouper 2.5+ can be helpful. 


How to Customize the Grouper User Interface (UI)

As of Grouper 2.2, most of the Grouper UI configuration is now in grouper-ui-ng.base.properties, and you can override it in the grouper-ui.properties file or database. Customizable string values that appear in the UI are defined in grouperText/grouper.textNg.en.us.base.properties, and you can override values in grouperText/grouper.text.en.us.properties or in the database configuration for "grouper.text.en.us.properties". See the Configuration Overlay page for details. 

Some settings for Grouper UI customization are described here. Look in grouper-ui-ng.base.properties for more settings in the UI.


Adding Custom CSS Files

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, colors, etc.

Custom stylesheets can be enumerated in grouper-ui property css.additional. This is a space separated list of one or more .css files which are inserted into the HEAD of all Grouper pages. The .css files are referenced in order and after any Grouper CSS files. This means that your CSS files can override any Grouper style definition.

Since pages in the UI have the base UI /grouperUi/app/UiV2Main.index, you can reference the root of the webapp with "../../".

Changing the Internet2 Logo

The grouper-ui property image.organisation-logo is the path to the organization logo that appears in the banner. The default value is grouperExternal/public/assets/images/organisation-logo.gif. To change this to your own image you can either:

a. Replace this image in your installation with a different image (in the container, the full path is /opt/grouper/grouperWebapp/grouperExternal/public/assets/images/organisation-logo.gif)

b. Add your image to your installation, and then update property image.organisation-logo with the new location. The Grouper UI engine will automatically prepend the value with "../../" in the HTML source, effectively the root of the Grouper webapp. Note that this also means that only relative locations work, not full internet URLs.

Note that the default template in Grouper only supports an image in the banner, not additional text.

Changing the Institution Name

The institution name that appears on the main page and in the footer is defined in the text resource bundle (grouperText/grouper.textNg.en.us.base.properties), property institutionName. You will likely want to change this from its default "Institute of Higher Education". This can be configured in overlay file grouperText/grouper.text.en.us.properties, or in the database configuration for config file "grouper.text.en.us.properties".

Changing the browser title

Set an env name in grouper.properties and use this in externalized text (v4.9.0+)

grouperAppName = PennGroups

browserTitleSuffix = , $$grouperAppName$$ ${edu.internet2.middleware.grouper.cfg.GrouperConfig.retrieveConfig().propertyValueString("grouper.env.name")}


Changing the Default Language

The Grouper UI supports internationalization for all navigational text and instructions. Text strings in UI templates are keyword pointers to the actual text in Java ResourceBundle files, which are property files stored in WEB-INF/classes/grouperText/grouper.textNg.<language>.<country>.base.properties. The default language is English (en_us), but a set of French translations (fr_fr) is packaged with Grouper.

The language resources and the default language can be changed in grouper.properties as in the example below. To add more resource bundles, add the file to directory WEB-INF/classes/grouperText/. In the example below, English and French languages would be available, and the preferred language as defined in the browser would be used.

grouper.text.defaultBundleIndex = 0

grouper.text.bundle.0.language = en
grouper.text.bundle.0.country = us
grouper.text.bundle.0.fileNamePrefix = grouperText/grouper.text.en.us
grouper.text.bundle.1.language = fr
grouper.text.bundle.1.country = fr
grouper.text.bundle.1.fileNamePrefix = grouperText/grouper.text.fr.fr


Changing UI Text

In the standard UI, all navigational text and instructions are derived from a Java ResourceBundle grouperText/grouper.textNg.en.us.base.properties (or other language equivalent). Custom properties to override default values can be added to overlay property file grouperText/grouper.text.en.us.properties, or a database configuration entry can be added to configuration "grouper.text.en.us.properties". Note that in the UI configuration, only overrides will be shown, not the large list of default values from the base properties file.

If you create your own JSP templates or Java classes, you are not under any obligation to use ResourceBundles instead of directly entering hard-coded text. However, if you wish to contribute code back to Grouper, such a contribution would be more useful if it used externalized text.

Legacy Settings Invalid for Grouper 2.5+


Information below this point is primarily relevant for the older "Admin UI" or "Lite UI", and generally will not apply to Grouper 2.5+


Some configuration that you generally will not need to change in upgrading to Grouper 2.2 is in media.properties. 

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:

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

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.

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:

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