I will put together a UI for lite group membership management

Here is a screen movie of the app, here is one with integration with another app.  To view, you need the free xvid codec

Features

Architecture

Note: if we are ok with this architecture, maybe some of the new features of Grouper could have screens here... it will be a lot easier for me to quickly make screens...

Coding samples

To make ajax work, there is a common method:

function ajax(theUrl, options) {

Note, there is no callback after the ajax is done (usually there is with business logic in Javascript), instead that is all handled on the server side.  So a button that calls ajax looks like this

    <a href="#" onclick="ajax('SimpleMembershipUpdate.deleteSingle?memberId=${theMember.uuid}'); return false;">The link</a>

Note that the class and method are listed there to reduce the levels of indirection (normally you will see a one-to-one mapping anyways).  The security restriction however is that the class must be in the edu.internet2.middleware.grouper.grouperUi.serviceLogic package, and the method must have this signature: public void methodName(HttpServletRequest, HttpServletResponse) Then in the Java side, you fill out an object model which consists of ordered actions to occur on screen.  This will be things like, replace this div with this JSP, change this form element value, hide this, show that, etc.

GuiResponseJs guiResponseJs = GuiResponseJs.retrieveGuiResponseJs();
guiResponseJs.addAction(GuiScreenAction.newAlert("The member was deleted: " + guiMember.getSubject().getDescription()));
guiResponseJs.addAction(GuiScreenAction.newInnerHtmlFromJsp("#simpleMembershipResultsList",
  "/WEB-INF/grouperUi/templates/simpleMembershipUpdate/simpleMembershipMembershipList.jsp"));

Note in that case the JSP is parsed during the web service call, and contents are shipped back in JSON in the guiResponseJs object model...

The rich client controls are easy to do as well, just have helper methods to set things up, e.g. a combobox:

<%-- note, this div will be the combobox --%>
<div id="simpleMembershipUpdateAddMember" style="width:400px;"></div>
<script>
guiRegisterDhtmlxCombo('simpleMembershipUpdateAddMember', 400,
true, "../app/SimpleMembershipUpdate.filterUsers" );
</script>

Then on the serverside, do a method, call helper methods to build the XML, and tell the controller not to send the GuiResponseJs JSON object:

  public void filterUsers(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {

    final Subject loggedInSubject = GrouperUiJ2ee.retrieveSubjectLoggedIn();


    GrouperSession grouperSession = null;

    String searchTerm = httpServletRequest.getParameter("mask");

    try {
      grouperSession = GrouperSession.start(loggedInSubject);


      Set<Subject> subjects = null;

      StringBuilder xmlBuilder = new StringBuilder(GuiUtils.DHTMLX_OPTIONS_START);

      if (StringUtils.defaultString(searchTerm).length() < 2) {
        GuiUtils.dhtmlxOptionAppend(xmlBuilder, "", "Enter 2 or more characters", null);
      } else {
        subjects = SubjectFinder.findAll(searchTerm);
      }

      for (Subject subject : GrouperUtil.nonNull(subjects)) {
        String value = GuiUtils.convertSubjectToValue(subject);

        String imageName = GuiUtils.imageFromSubjectSource(subject.getSource().getId());
        String label = GuiUtils.convertSubjectToLabel(subject);

        GuiUtils.dhtmlxOptionAppend(xmlBuilder, value, label, imageName);
      }

      xmlBuilder.append(GuiUtils.DHTMLX_OPTIONS_END);

      GuiUtils.printToScreen(xmlBuilder.toString(), "text/xml", false, false);

    } catch (Exception se) {
      throw new RuntimeException("Error searching for members: '" + searchTerm + "', " + se.getMessage(), se);
    } finally {
      GrouperSession.stopQuietly(grouperSession);
    }

    //dont print the regular JSON
    throw new ControllerDone();

  }

sda