UsersView.java

/*
** Module   : UsersView.java
** Abstract : UsersView is a view to manage user accounts.
**
** Copyright (c) 2017, Golden Code Development Corporation.
**
** -#- -I- --Date-- ----------------Description----------------------
** 001 SBI 20170601 Created initial version.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
** 
** Additional terms under GNU Affero GPL version 3 section 7:
** 
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
**   terms apply to the works covered under the License.  These additional terms
**   are non-permissive additional terms allowed under Section 7 of the GNU
**   Affero GPL version 3 and may not be removed by you.
** 
**   0. Attribution Requirement.
** 
**     You must preserve all legal notices or author attributions in the covered
**     work or Appropriate Legal Notices displayed by works containing the covered
**     work.  You may not remove from the covered work any author or developer
**     credit already included within the covered work.
** 
**   1. No License To Use Trademarks.
** 
**     This license does not grant any license or rights to use the trademarks
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
**     of Golden Code Development Corporation. You are not authorized to use the
**     name Golden Code, FWD, or the names of any author or contributor, for
**     publicity purposes without written authorization.
** 
**   2. No Misrepresentation of Affiliation.
** 
**     You may not represent yourself as Golden Code Development Corporation or FWD.
** 
**     You may not represent yourself for publicity purposes as associated with
**     Golden Code Development Corporation, FWD, or any author or contributor to
**     the covered work, without written authorization.
** 
**   3. No Misrepresentation of Source or Origin.
** 
**     You may not represent the covered work as solely your work.  All modified
**     versions of the covered work must be marked in a reasonable way to make it
**     clear that the modified work is not originating from Golden Code Development
**     Corporation or FWD.  All modified versions must contain the notices of
**     attribution required in this license.
*/


package com.goldencode.p2j.admin.client.application.home.accounts.users;


import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Set;

import com.google.gwt.user.client.ui.*;

import org.gwtbootstrap3.client.ui.Button;
import org.gwtbootstrap3.client.ui.TextBox;
import org.gwtbootstrap3.client.ui.gwt.DataGrid;

import com.goldencode.p2j.admin.TaggedName;
import com.goldencode.p2j.admin.client.NameTokens;
import com.goldencode.p2j.admin.client.application.home.BaseViewWithUiHandlers;
import com.goldencode.p2j.admin.client.widget.GridHandle;
import com.goldencode.p2j.admin.client.widget.GridHelper;
import com.goldencode.p2j.admin.shared.PaperFormat;
import com.goldencode.p2j.admin.shared.PaperOrientation;
import com.goldencode.p2j.admin.shared.ReportParameters;
import com.goldencode.p2j.admin.shared.Reports;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.inject.Inject;


/**
 * UsersView is a view to manage user accounts.
 */
public class UsersView
extends BaseViewWithUiHandlers<UsersUIHandlers>
implements UsersPresenter.MyView
{
   /**
    * GWT UI creator.
    */
   interface Binder
   extends UiBinder<Widget, UsersView>
   {}
   
   /** The Refresh button */
   @UiField
   Button refresh;

   /** The Add User button */
   @UiField
   Button addUserAccount;

   /** The Delete User button */
   @UiField
   Button deleteUserAccount;

   /** The Clone User button */
   @UiField
   Button cloneUserAccount;

   /** The Edit User button */
   @UiField
   Button editUserAccount;
   
   /** The Disable/Enable User button */
   @UiField
   Button disableUserAccount;
   
   /** The Find User button */
   @UiField
   Button findUser;

   /** The Switch to ACLs button */
   @UiField
   Button switchToACLs;

   /** The Delete Group button */
   @UiField
   Button deleteGroup;
   
   /** The Assign New Group button */
   @UiField
   Button assignNewGroup;
   
   /** The New Group button */
   @UiField
   TextBox newGroup;

   /** The Populate all groups button */
   @UiField
   Button populateGroups;
   
   /** The Add Selected button */
   @UiField
   Button addSelected;
   
   /** The filter text box to select groups according to the given criteria */
   @UiField
   TextBox filterSelector;
   
   /** The Filter groups button */
   @UiField
   Button filterGroups;
   
   /** The all users table view */
   @UiField(provided = true)
   DataGrid<TaggedName> usersGrid = GridHelper.create();
   
   /** The user's assigned groups table view */
   @UiField(provided = true)
   DataGrid<TaggedName> userGroupsGrid = GridHelper.create();
   
   /** The all existing groups table view */
   @UiField(provided = true)
   DataGrid<TaggedName> allGroupsGrid = GridHelper.create();
   
   /** The modal container */
   @UiField
   HasWidgets modalFragment;

   /** The parent container for the Add/Edit User Definition view */
   @UiField
   FlowPanel userDefModal;


   /** The column names for the all users table. */
   private final String[] usersColumnNames = { "User ID", "Person", "OS User ID" };
   
   /** The column names for the user's groups and all groups tables */ 
   private final String[] userGroupsColumnNames = { "Group ID", "Description" };
   
   /** The all users grid handle */
   private final GridHandle<TaggedName> usersGridHandle;
   
   /** The all users data provider */
   private final ListDataProvider<TaggedName> usersProvider;
   
   /** The all users selection model */
   private final MultiSelectionModel<TaggedName> usersSelectionModel;
   
   /** The user's assigned groups grid handle */
   private final GridHandle<TaggedName> userGroupsGridHandle;
   
   /** The user's assigned groups data provider */
   private final ListDataProvider<TaggedName> userGroupsProvider;
   
   /** The user's assigned groups selection model */
   private final MultiSelectionModel<TaggedName> userGroupsSelectionModel;
   
   /** The all groups grid handle */
   private final GridHandle<TaggedName> allGroupsGridHandle;
   
   /** The all groups data provider */
   private final ListDataProvider<TaggedName> allGroupsProvider;
   
   /** The all groups selection model */
   private final MultiSelectionModel<TaggedName> allGroupsSelectionModel;
   
   /** The Clone User Account View */
   private final DeletingUsersAlert deletingUsersAlert;
   
   /** The  */
   private final CloneUserAccountView cloneUserAccountView;
   
   /** The Removing Groups Alert */
   private final RemovingGroupsAlert removingGroupsAlert;
   
   /**
    * Constructs this view, used by MPV gwtplatform of ArcBees Inc.
    * 
    * @param    binder
    *           UI creator
    * @param    deletingUsersAlert
    *           Deleting user alert child view
    * @param    cloneUserAccountView
    *           Clone user account child view
    * @param    removingGroupsAlert
    *           Removing groups alert child view
    */
   @Inject
   public UsersView(Binder binder,
                    DeletingUsersAlert deletingUsersAlert,
                    CloneUserAccountView cloneUserAccountView,
                    RemovingGroupsAlert removingGroupsAlert)
   {
      initWidget(binder.createAndBindUi(this));
      
      this.deletingUsersAlert = deletingUsersAlert;
      this.cloneUserAccountView = cloneUserAccountView;
      this.removingGroupsAlert = removingGroupsAlert;
      
      this.usersGridHandle = initTable(usersGrid, usersColumnNames, true, true);
      this.usersProvider = this.usersGridHandle.getDataProvider();
      this.usersSelectionModel = (MultiSelectionModel<TaggedName>)this.usersGridHandle.getSelectionModel();
      this.usersSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler()
      {
         @Override
         public void onSelectionChange(SelectionChangeEvent event)
         {
            Set<TaggedName> selected = UsersView.this.usersSelectionModel.getSelectedSet();
            logger.info("usersSelectionModel: onSelectionChange: " + Arrays.toString(selected.stream().map(tn -> tn.getName()).toArray()));
            if (selected.size() != 1)
            {
               UsersView.this.userGroupsSelectionModel.clear();
            }
            UsersView.this.getUiHandlers().onUserSelected(selected);
         }
      });
      
      this.userGroupsGridHandle = initTable(userGroupsGrid, userGroupsColumnNames, true, true);
      this.userGroupsProvider = this.userGroupsGridHandle.getDataProvider();
      this.userGroupsSelectionModel = (MultiSelectionModel<TaggedName>)this.userGroupsGridHandle.getSelectionModel();
      this.userGroupsSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler()
      {
         @Override
         public void onSelectionChange(SelectionChangeEvent event)
         {
            Set<TaggedName> selected = UsersView.this.userGroupsSelectionModel.getSelectedSet();
            logger.info("userGroupsSelectionModel: onSelectionChange: " + Arrays.toString(selected.stream().map(tn -> tn.getName()).toArray()));
            UsersView.this.getUiHandlers().onGroupSelected(selected);
         }
      });

      this.allGroupsGridHandle = initTable(allGroupsGrid, userGroupsColumnNames, true, true);
      this.allGroupsProvider = this.allGroupsGridHandle.getDataProvider();
      this.allGroupsSelectionModel = (MultiSelectionModel<TaggedName>)this.allGroupsGridHandle.getSelectionModel();
      this.allGroupsSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler()
      {
         @Override
         public void onSelectionChange(SelectionChangeEvent event)
         {
            if (UsersView.this.usersSelectionModel.getSelectedSet().size() == 1)
            {
               Set<TaggedName> selected = UsersView.this.allGroupsSelectionModel.getSelectedSet();
               List<TaggedName> userGroups = UsersView.this.userGroupsProvider.getList();
               userGroups.forEach(group -> UsersView.this.allGroupsSelectionModel.setSelected(group, true));

               UsersView.this.addSelected.setEnabled(!userGroups.containsAll(selected));
            }
            else
            {
               UsersView.this.allGroupsSelectionModel.clear();
            }
         }
      });
      
      ((com.google.gwt.user.cellview.client.DataGrid<TaggedName>) usersGrid).setMinimumTableWidth(300, Unit.PX);
      setupModalSlot(UsersPresenter.MODAL_CONTENT, modalFragment);
      addDialog(deletingUsersAlert);
      addDialog(cloneUserAccountView);
      addDialog(removingGroupsAlert);
   }
   
   /**
    * Setups the given table view.
    * 
    * @param    grid
    *           The given table view
    * @param    columnNames
    *           The column names
    * @param    multiSelection
    *           The true value indicates that the multiple rows selection is supported, otherwise
    *           the single selection is supported only. 
    * @param    sortable
    *           The true value indicates that the table columns supports changing their sort order
    *           from descending to ascending and vice versa.
    * 
    * @return   The grid handle
    */
   private GridHandle<TaggedName> initTable(DataGrid<TaggedName> grid,
                                            String[] columnNames,
                                            boolean multiSelection,
                                            boolean sortable)
   {
//      grid.setSelectionModel(selectionModel);
//      grid.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
//      grid.setKeyboardPagingPolicy(KeyboardPagingPolicy.CURRENT_PAGE);
//      
//      grid.addCellPreviewHandler(new CellPreviewEvent.Handler<TaggedName>()
//      {
//         @Override
//         public void onCellPreview(CellPreviewEvent<TaggedName> event)
//         {
//            NativeEvent nevt = event.getNativeEvent();
//            
//            int keyCode = nevt.getKeyCode();
//             if (keyCode == KeyCodes.KEY_UP     || keyCode == KeyCodes.KEY_DOWN ||
//                 keyCode == KeyCodes.KEY_LEFT   || keyCode == KeyCodes.KEY_RIGHT ||
//                 keyCode == KeyCodes.KEY_PAGEUP || keyCode == KeyCodes.KEY_PAGEDOWN)
//             {
//                int rowIndex = grid.getKeyboardSelectedRow();
//                TaggedName row = dataProvider.getList().get(rowIndex);
//                selectionModel.setSelected(row, true);
//             }
//         }});
      //noinspection NonJREEmulationClassesInClientCode
      GridHelper.ColumnInfo<TaggedName> col1 = new GridHelper.ColumnInfo<TaggedName>(
               columnNames[0],
               (tn) -> tn.getName(),
               (sortable) ? (tn1, tn2) -> tn1.getName().compareTo(tn2.getName()) : null
               );
      //noinspection NonJREEmulationClassesInClientCode
      GridHelper.ColumnInfo<TaggedName> col2 = new GridHelper.ColumnInfo<TaggedName>(
         columnNames[1],
         (tn) -> tn.getTag(),
         (sortable) ? (tn1, tn2) -> tn1.getTag().compareTo(tn2.getTag()) : null
      );
      //noinspection NonJREEmulationClassesInClientCode
      GridHelper.ColumnInfo<TaggedName> col3 = new GridHelper.ColumnInfo<TaggedName>(
         columnNames[2],
         (tn) -> tn.get(2),
         (sortable) ? (tn1, tn2) -> tn1.getTag().compareTo(tn2.get(2)) : null
      );
      
      return GridHelper.initListGrid(grid, multiSelection, sortable, col1, col2, col3);
   }

   private void updateDataProvider(DataGrid<TaggedName> grid,
                                   ListDataProvider<TaggedName> dataProvider,
                                   TaggedName[] records)
   {
      GridHelper.updateDataProvider(grid, dataProvider, records);
   }
   
   /**
    * Setups the table data provider.
    * 
    * @param    dataGrid
    *           The given table view
    * @param    dataProvider
    *           The given table data provider
    * @param    records
    *           The data
    * @param    filterExp
    *           The regular expression to select data satisfying the provided criteria
    */
   private void updateDataProvider(DataGrid<TaggedName> dataGrid,
                                   ListDataProvider<TaggedName> dataProvider,
                                   TaggedName[] records,
                                   RegExp filterExp)
   {
      dataProvider.getList().clear();
      
      if (records == null)
      {
         return;
      }
      
      GridHelper.updateDataProvider(dataGrid, dataProvider, records, filterExp);
   }

   /**
    * Sets the all users table's model with users accounts data given by tagged names
    * 
    * @param    users
    *           The array of users accounts given by tagged names
    */
   @Override
   public void setUsers(TaggedName[] users)
   {
      logger.info("setGroups " + users);
      usersSelectionModel.clear();
      updateDataProvider(usersGrid, usersProvider, users);
   }

   /**
    * The Refresh button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("refresh")
   void onRefresh(ClickEvent e)
   {
      getUiHandlers().onRefresh();
   }
   
   /**
    * The Add User button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("addUserAccount")
   void onAddUserAccount(ClickEvent e)
   {
      logger.info("onAddUserAccount");
      getUiHandlers().showAddUserAccountDialog();
   }
   
   /**
    * The Clone User button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("cloneUserAccount")
   void onCloneUserAccount(ClickEvent e)
   {
      TaggedName[] users = UsersView.this.usersSelectionModel.getSelectedSet().toArray(new TaggedName[0]);
      getUiHandlers().setupCloneUserAccountDialog(users[0]);
   }
   
   /**
    * The Edit User button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("editUserAccount")
   void onEditUserAccount(ClickEvent e)
   {
      logger.info("onEditUserAccount");
      TaggedName[] users = UsersView.this.usersSelectionModel.getSelectedSet().toArray(new TaggedName[0]);
      getUiHandlers().showEditUserAccountDialog(users[0]);
   }
   
   /**
    * The Populate button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("populateGroups")
   void onPopulateGroups(ClickEvent e)
   {
      getUiHandlers().populateAndRefreshAllGroups();
   }
   
   /**
    * The Delete User button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("deleteUserAccount")
   void onDeleteUserAccount(ClickEvent e)
   {
      logger.info("onDeleteUserAccount");
      TaggedName[] users = this.usersSelectionModel.getSelectedSet().toArray(new TaggedName[0]);
      
      getUiHandlers().setupDeletingUsersAlert(users);
   }
   
   /**
    * The Find User button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("findUser")
   void onFindUserAccount(ClickEvent e)
   {
      logger.info("onFindUserAccount");
      
      getUiHandlers().setupFindUserAccountDialog();
   }

   /**
    * The Delete Group button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("deleteGroup")
   void onDeleteGroup(ClickEvent e)
   {
      logger.info("onDeleteGroup");

      TaggedName[] users = this.usersSelectionModel.getSelectedSet().toArray(new TaggedName[0]);
      TaggedName[] groups = this.userGroupsSelectionModel.getSelectedSet().toArray(new TaggedName[0]);
      
      getUiHandlers().setupRemovingGroupsAlert(users[0], groups);
   }
   
   /**
    * The Assign New Group button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("assignNewGroup")
   void onAssignNewGroup(ClickEvent e)
   {
      logger.info("onAssignNewGroup");

      TaggedName[] users = this.usersSelectionModel.getSelectedSet().toArray(new TaggedName[0]);
      //TODO validation
      getUiHandlers().addUserInNewGroup(users[0], newGroup.getText());
   }
   
   /**
    * The Filter button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("filterGroups")
   void onFilterGroups(ClickEvent e)
   {
      logger.info("onFilterGroups");
      //TODO input validation
      getUiHandlers().setGroupsFilter(filterSelector.getText());
   }
   
   /**
    * The Add Selected button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("addSelected")
   void onAddSelected(ClickEvent e)
   {
      logger.info("AddSelected");
      List<TaggedName> userGroups = UsersView.this.userGroupsProvider.getList();
      Set<TaggedName> selected = this.allGroupsSelectionModel.getSelectedSet();
      selected.removeAll(userGroups);
      TaggedName[] additionalGroups = selected.toArray(new TaggedName[0]);
      TaggedName user = this.usersSelectionModel.getSelectedSet().toArray(new TaggedName[0])[0];

      getUiHandlers().addUserToGroups(user, additionalGroups);
   }
   
   /**
    * Sets the user's groups table's model with all assigned groups for the single selected
    * user from the all users table view.
    * 
    * @param    groups
    *           The array of assigned groups given by tagged names
    */
   @Override
   public void setAssignedGroups(TaggedName[] groups)
   {
      logger.info("setAssignedGroups");
      this.userGroupsSelectionModel.clear();
      this.allGroupsSelectionModel.clear();
      updateDataProvider(userGroupsGrid, userGroupsProvider, groups);
      
      List<TaggedName> userGroups = UsersView.this.userGroupsProvider.getList();
      userGroups.forEach(group -> UsersView.this.allGroupsSelectionModel.setSelected(group, true));
      
      UsersView.this.addSelected.setEnabled(false);
   }

   /**
    * Sets the all groups accounts table's model with all existing groups accounts.
    *  
    * @param    allGroups
    *           The array of all existing groups accounts
    * @param    filterExp
    *           The regular expression to select groups with accounts names according to this
    *           filter criteria
    */
   @Override
   public void setAllGroups(TaggedName[] allGroups, RegExp filterExp)
   {
      updateDataProvider(allGroupsGrid, allGroupsProvider,
               allGroups, filterExp);
      if (UsersView.this.usersSelectionModel.getSelectedSet().size() == 1)
      {
         List<TaggedName> userGroups = UsersView.this.userGroupsProvider.getList();
         userGroups.forEach(group -> UsersView.this.allGroupsSelectionModel.setSelected(group, true));
      }
      else
      {
         UsersView.this.allGroupsSelectionModel.clear();
      }
   }

   /**
    * Gets a reference to the Deleting Users Alert.
    * 
    * @return   The Deleting Users Alert
    */
   @Override
   public DeletingUsersAlert getDeletingUsersAlert()
   {
      return deletingUsersAlert;
   }

   /**
    * Gets a reference to the Clone User Account View.
    * 
    * @return   The Clone User Account View
    */
   @Override
   public CloneUserAccountView getCloneUserAccountView()
   {
      return cloneUserAccountView;
   }

   /**
    * Provides all components that must be managed by its view state machine.
    * 
    * @return   The array of components managed by its view state machine
    */
   @Override
   public HasEnabled[] get()
   {
      return new HasEnabled[] {
               refresh,
               addUserAccount,
               deleteUserAccount,
               cloneUserAccount,
               editUserAccount,
               disableUserAccount,
               findUser,
               switchToACLs,
               
               newGroup,
               assignNewGroup,
               deleteGroup,
               
               populateGroups,
               addSelected,
               filterSelector,
               filterGroups
      };
   }

   /**
    * Gets a reference to the Removing Groups Alert.
    * 
    * @return   The Removing Groups Alert
    */
   @Override
   public RemovingGroupsAlert getRemovingGroupsAlert()
   {
      return removingGroupsAlert;
   }

   /**
    * Tests if this group has been selected from the user's assigned groups table.
    * 
    * @param    group
    *           The target group account given by its tagged name
    * 
    * @return   True iff the target group has been selected, otherwise false.
    */
   @Override
   public boolean isSelectedFromUserGroups(TaggedName group)
   {
      return userGroupsSelectionModel.isSelected(group);
   }

   /**
    * Tests if these tested users have been selected from the all users table.
    * 
    * @param    users
    *           The array of tested user accounts given by tagged names
    * 
    * @return   True if all tested users have been selected, otherwise false.
    */
   @Override
   public boolean isSelectedFromUsers(TaggedName[] users)
   {
      return usersSelectionModel.getSelectedSet().containsAll(Arrays.asList(users));
   }

   /**
    * Returns the parent container where the Add/Edit User Definition View will be revealed in.
    * 
    * @return   The parent container for the Add/Edit User Definition View.
    */
   @Override
   public HasWidgets getUserDefModalParent()
   {
      return userDefModal;
   }

   /**
    * Selects the user's data row from the all users table by a given user's tagged name.
    * 
    * @param    user
    *           The user account given by its tagged name
    */
   @Override
   public void selectUser(TaggedName user)
   {
      logger.info("setSelectedUser " + user.getName());
      usersSelectionModel.clear();
      userGroupsSelectionModel.clear();
      Scheduler.get().scheduleDeferred(new ScheduledCommand()
      {
         @Override
         public void execute()
         {
            usersSelectionModel.setSelected(user, true);
            scrollIntoView(user);
         }
      });
   }
   
   /**
    * Sets the row with the given user within the current view.
    * 
    * @param    user
    *           The user account given by its tagged name
    */
   private void scrollIntoView(TaggedName user)
   {
      int index = usersProvider.getList().indexOf(user);
      if (index >= 0)
      {
         usersGrid.getRowElement(index).scrollIntoView();
      }
   }
   
   /**
    * Selects the user's data row from the all users table by a given user's account name.
    * 
    * @param    name
    *           The account name.
    */
   @Override
   public void selectUserByName(String name)
   {
      logger.info("setSelectedUser " + name);
      usersSelectionModel.clear();
      userGroupsSelectionModel.clear();
      usersProvider.getList().forEach(user -> {
         if (user.getName().equals(name))
         {
            Scheduler.get().scheduleDeferred(new ScheduledCommand()
            {
               @Override
               public void execute()
               {
                  usersSelectionModel.setSelected(user, true);
                  scrollIntoView(user);
               }
            });
         }
      });
      
   }

   /**
    * Changes the Populate button's label from "Populate" to "Refresh"
    * 
    * @param    populated
    *           The flag which true value means to change the Populate label to Refresh one
    */
   @Override
   public void togglePopulateGroupsButton(boolean populated)
   {
      if (populated)
      {
         populateGroups.setText("Refresh");
      }
   }

   /**
    * Toggles the Disable/Enable User button's label.
    * 
    * @param    enabled
    *           Indicates the user's enabled state. The true value switches this button's label
    *           from "Enable User" to "Disable User" and vice versa the false value switches
    *           from "Disable User" to "Enable User".
    */
   @Override
   public void toggleDisableUserButton(boolean enabled)
   {
      if (enabled)
      {
         disableUserAccount.setText("Disable User");
      }
      else
      {
         disableUserAccount.setText("Enable User");
      }
   }

   /**
    * Tests if the tested user has been selected from the all users table.
    * 
    * @param    user
    *           The tested user account given by its tagged name
    * 
    * @return   True if the tested user has been selected, otherwise false.
    */
   @Override
   public boolean isSelectedFromUsers(TaggedName user)
   {
      return usersSelectionModel.getSelectedSet().contains(user);
   }
   
   @UiHandler("disableUserAccount")
   public void onDisableUserAccount(ClickEvent e)
   {
      TaggedName user = usersSelectionModel.getSelectedSet().toArray(new TaggedName[0])[0];
      getUiHandlers().disableUserAccount(user);
   }

   /**
    * Tests if these tested groups have been selected from the user's assigned groups table.
    * 
    * @param    groups
    *           The array of tested groups given by tagged names
    * 
    * @return   True iff the tested groups have been selected, otherwise false.
    */
   @Override
   public boolean isSelectedFromUserGroups(TaggedName[] groups)
   {
      return userGroupsSelectionModel.getSelectedSet().containsAll(Arrays.asList(groups));
   }

   /**
    * Tests if these tested groups have been selected from the all groups table.
    * 
    * @param    groups
    *           The array of tested groups given by tagged names
    * 
    * @return   True iff the tested groups have been selected, otherwise false.
    */
   @Override
   public boolean isSelectedFromAllGroups(TaggedName[] groups)
   {
      return allGroupsSelectionModel.getSelectedSet().containsAll(Arrays.asList(groups));
   }
   
   /**
    * The Switch To ACLs button click handler.
    * 
    * @param    e
    *           Click event
    */
   @UiHandler("switchToACLs")
   void onSwitchToACLs(ClickEvent e)
   {
      logger.info("onSwitchToACLs");
      TaggedName selected = UsersView.this.usersSelectionModel.getSelectedSet().toArray(new TaggedName[0])[0];
      getUiHandlers().switchToACLs(selected);
   }

   /**
    * Returns pairs of a report parameter key and its value
    * 
    * @return   The enumeration map of report parameters keys to their values
    */
   @Override
   public EnumMap<ReportParameters, Object> getReportParameters()
   {
      EnumMap<ReportParameters, Object> parameters = new EnumMap<ReportParameters, Object>(
               ReportParameters.class);
      parameters.put(ReportParameters.SELECTION, usersSelectionModel.getSelectedSet().toArray(
               new TaggedName[usersSelectionModel.getSelectedSet().size()]));
      parameters.put(ReportParameters.TITLE, "Users");
      parameters.put(ReportParameters.REPORT_ID, Reports.USERS);
      parameters.put(ReportParameters.PAPER_FORMAT, PaperFormat.A4);
      parameters.put(ReportParameters.PAPER_ORIENT, PaperOrientation.PORTRAIT);
      
      return parameters;
   }

   /**
    * Returns the size of all data to preview or the size of selected data to preview
    * 
    * @param    range
    *           The target range of selected data to preview
    * @return   The size of the selected data
    */
   @Override
   public int getDataSize(Range range)
   {
      if (range == Range.SELECTION)
      {
         return usersSelectionModel.getSelectedSet().size();
      }
      
      return usersProvider.getList().size();
   }

   /**
    * Returns the value of the preview dialog parameter.
    * 
    * @param    parameter
    *           The preview dialog parameter key
    * 
    * @return   The value of the preview dialog parameter
    */
   @Override
   public String getPreviewDialogParameter(PreviewDialogParameters parameter)
   {
      int selectedRecordsNumber = usersSelectionModel.getSelectedSet().size();
      int allRecordsNumber = usersProvider.getList().size();
      switch (parameter)
      {
         case HELP_TEXT:
            String hlpText;
            if (selectedRecordsNumber > 0)
            {
               hlpText = "<h4>Please refine your request.</h4>";
            }
            else
            {
               if (allRecordsNumber > 1)
               {
                  hlpText = "<h4>" + allRecordsNumber + " user accounts being printed. Continue?</h4>";
               }
               else if (allRecordsNumber == 1)
               {
                  hlpText = "<h4>1 user account being printed. Continue?</h4>";
               }
               else
               {
                  hlpText = "<h4>There are no users to print. Continue?</h4>";
               }
            }
            
            return hlpText;
         case TITLE:
            return "Printing Users";
         case RANGE_ALL_LABEL:
            if (allRecordsNumber > 2)
            {
               return "all " + allRecordsNumber + " user accounts";
            }
            else if (allRecordsNumber == 2)
            {
               return "both user accounts";
            }
            
            break;
         case RANGE_SELECTION_LABEL:
            if (selectedRecordsNumber > 1)
            {
               return selectedRecordsNumber + " selected user accounts";
            }
            else if (selectedRecordsNumber == 1)
            {
               return "the selected user account";
            }
            
            break;
      }
      
      return null;
   }

   /**
    * Returns url path to this view.
    * 
    * @return   The url path.
    */
   @Override
   public String getPathToPrintPreviewOwner()
   {
      return NameTokens.USERS;
   }
}