@Override
  protected void showListOfExistingObjects(@Nonnull final WebPageExecutionContext aWPEC) {
    final Locale aDisplayLocale = aWPEC.getDisplayLocale();
    final HCNodeList aNodeList = aWPEC.getNodeList();

    // Toolbar on top
    final IButtonToolbar<?> aToolbar = aNodeList.addAndReturnChild(getStyler().createToolbar());
    aToolbar.addButtonNew(
        EText.BUTTON_CREATE_NEW_USER.getDisplayText(aDisplayLocale), createCreateURL(aWPEC));

    final ITabBox<?> aTabBox = getStyler().createTabBox();

    final AccessManager aMgr = AccessManager.getInstance();

    final Collection<? extends IUser> aActiveUsers = aMgr.getAllActiveUsers();
    aTabBox.addTab(
        EText.TAB_ACTIVE.getDisplayTextWithArgs(
            aDisplayLocale, Integer.toString(aActiveUsers.size())),
        getTabWithUsers(aDisplayLocale, aActiveUsers, getID() + "1"));

    final Collection<? extends IUser> aDisabledUsers = aMgr.getAllDisabledUsers();
    aTabBox.addTab(
        EText.TAB_DISABLED.getDisplayTextWithArgs(
            aDisplayLocale, Integer.toString(aDisabledUsers.size())),
        getTabWithUsers(aDisplayLocale, aDisabledUsers, getID() + "2"));

    final Collection<? extends IUser> aDeletedUsers = aMgr.getAllDeletedUsers();
    aTabBox.addTab(
        EText.TAB_DELETED.getDisplayTextWithArgs(
            aDisplayLocale, Integer.toString(aDeletedUsers.size())),
        getTabWithUsers(aDisplayLocale, aDeletedUsers, getID() + "3"));
    aNodeList.addChild(aTabBox);
  }
  @Nonnull
  protected IHCNode getTabWithUsers(
      @Nonnull final Locale aDisplayLocale,
      @Nonnull final Collection<? extends IUser> aUsers,
      @Nonnull @Nonempty final String sTableID) {
    final boolean bSeparateLoginName = !useEmailAddressAsLoginName();
    final AccessManager aMgr = AccessManager.getInstance();
    // List existing
    final List<HCCol> aCols = new ArrayList<HCCol>();
    aCols.add(new HCCol(200));
    if (bSeparateLoginName) aCols.add(new HCCol(200));
    aCols.add(HCCol.star());
    aCols.add(new HCCol(150));
    aCols.add(createActionCol(3));
    final IHCTable<?> aTable =
        getStyler().createTable(ArrayHelper.newArray(aCols, HCCol.class)).setID(sTableID);
    final HCRow aHeaderRow = aTable.addHeaderRow();
    aHeaderRow.addCell(EText.HEADER_NAME.getDisplayText(aDisplayLocale));
    if (bSeparateLoginName)
      aHeaderRow.addCell(EText.HEADER_LOGINNAME.getDisplayText(aDisplayLocale));
    aHeaderRow.addCells(
        EText.HEADER_EMAIL.getDisplayText(aDisplayLocale),
        EText.HEADER_USERGROUPS.getDisplayText(aDisplayLocale),
        EWebBasicsText.MSG_ACTIONS.getDisplayText(aDisplayLocale));

    for (final IUser aCurUser : aUsers) {
      final ISimpleURL aViewLink = createViewURL(aCurUser);

      final HCRow aRow = aTable.addBodyRow();
      aRow.addCell(
          new HCA(aViewLink).addChild(SecurityUI.getUserDisplayName(aCurUser, aDisplayLocale)));
      if (bSeparateLoginName) aRow.addCell(new HCA(aViewLink).addChild(aCurUser.getLoginName()));
      aRow.addCell(new HCA(aViewLink).addChild(aCurUser.getEmailAddress()));

      // User groups
      final Collection<IUserGroup> aUserGroups =
          aMgr.getAllUserGroupsWithAssignedUser(aCurUser.getID());
      final StringBuilder aUserGroupsStr = new StringBuilder();
      for (final IUserGroup aUserGroup :
          ContainerHelper.getSorted(
              aUserGroups, new ComparatorHasName<IUserGroup>(aDisplayLocale))) {
        if (aUserGroupsStr.length() > 0) aUserGroupsStr.append(", ");
        aUserGroupsStr.append(aUserGroup.getName());
      }
      aRow.addCell(new HCA(aViewLink).addChild(aUserGroupsStr.toString()));

      final IHCCell<?> aActionCell = aRow.addCell();

      // Edit user
      if (isEditAllowed(aCurUser)) aActionCell.addChild(createEditLink(aCurUser, aDisplayLocale));
      else aActionCell.addChild(createEmptyAction());

      // Copy
      aActionCell.addChild(createCopyLink(aCurUser, aDisplayLocale));

      // Reset password of user
      if (canResetPassword(aCurUser)) {
        aActionCell.addChild(
            new HCA(
                    LinkUtils.getSelfHref()
                        .add(CHCParam.PARAM_ACTION, ACTION_RESET_PASSWORD)
                        .add(CHCParam.PARAM_OBJECT, aCurUser.getID()))
                .setTitle(
                    EText.TITLE_RESET_PASSWORD.getDisplayTextWithArgs(
                        aDisplayLocale, SecurityUI.getUserDisplayName(aCurUser, aDisplayLocale)))
                .addChild(getResetPasswordIcon()));
      } else aActionCell.addChild(createEmptyAction());
    }

    final HCNodeList aNodeList = new HCNodeList();
    aNodeList.addChild(aTable);

    final DataTables aDataTables = getStyler().createDefaultDataTables(aTable, aDisplayLocale);
    aDataTables.getOrCreateColumnOfTarget(3).addClass(CSS_CLASS_ACTION_COL).setSortable(false);
    aDataTables.setInitialSorting(1, ESortOrder.ASCENDING);
    aNodeList.addChild(aDataTables);

    // Required for best layout inside a tab!
    aTable.removeAllColumns();

    return aNodeList;
  }
  @Override
  @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
  protected void showSelectedObject(
      @Nonnull final WebPageExecutionContext aWPEC, @Nonnull final IUser aSelectedObject) {
    final HCNodeList aNodeList = aWPEC.getNodeList();
    final Locale aDisplayLocale = aWPEC.getDisplayLocale();
    final AccessManager aMgr = AccessManager.getInstance();
    final IHCTableFormView<?> aTable =
        aNodeList.addAndReturnChild(getStyler().createTableFormView(new HCCol(170), HCCol.star()));
    aTable.setSpanningHeaderContent(
        EText.HEADER_DETAILS.getDisplayTextWithArgs(
            aDisplayLocale, SecurityUI.getUserDisplayName(aSelectedObject, aDisplayLocale)));
    onShowSelectedObjectTableStart(aTable, aSelectedObject, aDisplayLocale);
    if (!useEmailAddressAsLoginName()) {
      aTable
          .createItemRow()
          .setLabel(EText.LABEL_LOGINNAME.getDisplayText(aDisplayLocale))
          .setCtrl(aSelectedObject.getLoginName());
    }
    aTable
        .createItemRow()
        .setLabel(EText.LABEL_FIRSTNAME.getDisplayText(aDisplayLocale))
        .setCtrl(aSelectedObject.getFirstName());
    aTable
        .createItemRow()
        .setLabel(EText.LABEL_LASTNAME.getDisplayText(aDisplayLocale))
        .setCtrl(aSelectedObject.getLastName());
    aTable
        .createItemRow()
        .setLabel(EText.LABEL_EMAIL.getDisplayText(aDisplayLocale))
        .setCtrl(getStyler().createEmailLink(aSelectedObject.getEmailAddress()));
    aTable
        .createItemRow()
        .setLabel(EText.LABEL_ENABLED.getDisplayText(aDisplayLocale))
        .setCtrl(EWebBasicsText.getYesOrNo(aSelectedObject.isEnabled(), aDisplayLocale));
    aTable
        .createItemRow()
        .setLabel(EText.LABEL_DELETED.getDisplayText(aDisplayLocale))
        .setCtrl(EWebBasicsText.getYesOrNo(aSelectedObject.isDeleted(), aDisplayLocale));
    aTable
        .createItemRow()
        .setLabel(EText.LABEL_LAST_LOGIN.getDisplayText(aDisplayLocale))
        .setCtrl(
            aSelectedObject.getLastLoginDateTime() != null
                ? new HCTextNode(
                    PDTToString.getAsString(aSelectedObject.getLastLoginDateTime(), aDisplayLocale))
                : HCEM.create(EText.LABEL_LAST_LOGIN_NEVER.getDisplayText(aDisplayLocale)));
    aTable
        .createItemRow()
        .setLabel(EText.LABEL_LOGIN_COUNT.getDisplayText(aDisplayLocale))
        .setCtrl(Integer.toString(aSelectedObject.getLoginCount()));
    aTable
        .createItemRow()
        .setLabel(EText.LABEL_CONSECUTIVE_FAILED_LOGIN_COUNT.getDisplayText(aDisplayLocale))
        .setCtrl(Integer.toString(aSelectedObject.getConsecutiveFailedLoginCount()));

    // user groups
    final Collection<IUserGroup> aUserGroups =
        aMgr.getAllUserGroupsWithAssignedUser(aSelectedObject.getID());
    if (aUserGroups.isEmpty()) {
      aTable
          .createItemRow()
          .setLabel(EText.LABEL_USERGROUPS_0.getDisplayText(aDisplayLocale))
          .setCtrl(HCEM.create(EText.NONE_DEFINED.getDisplayText(aDisplayLocale)));
    } else {
      final HCNodeList aUserGroupUI = new HCNodeList();
      for (final IUserGroup aUserGroup :
          ContainerHelper.getSorted(aUserGroups, new ComparatorHasName<IUserGroup>(aDisplayLocale)))
        aUserGroupUI.addChild(HCDiv.create(aUserGroup.getName()));
      aTable
          .createItemRow()
          .setLabel(
              EText.LABEL_USERGROUPS_N.getDisplayTextWithArgs(
                  aDisplayLocale, Integer.toString(aUserGroups.size())))
          .setCtrl(aUserGroupUI);
    }

    // roles
    final Set<IRole> aUserRoles = aMgr.getAllUserRoles(aSelectedObject.getID());
    if (aUserRoles.isEmpty()) {
      aTable
          .createItemRow()
          .setLabel(EText.LABEL_ROLES_0.getDisplayText(aDisplayLocale))
          .setCtrl(HCEM.create(EText.NONE_DEFINED.getDisplayText(aDisplayLocale)));
    } else {
      final HCNodeList aRoleUI = new HCNodeList();
      for (final IRole aRole :
          ContainerHelper.getSorted(aUserRoles, new ComparatorHasName<IRole>(aDisplayLocale)))
        aRoleUI.addChild(HCDiv.create(aRole.getName()));
      aTable
          .createItemRow()
          .setLabel(
              EText.LABEL_ROLES_N.getDisplayTextWithArgs(
                  aDisplayLocale, Integer.toString(aUserRoles.size())))
          .setCtrl(aRoleUI);
    }

    // custom attributes
    final Map<String, Object> aCustomAttrs = aSelectedObject.getAllAttributes();

    // Callback
    final Set<String> aHandledAttrs =
        showCustomAttrsOfSelectedObject(aSelectedObject, aCustomAttrs, aTable, aDisplayLocale);

    if (!aCustomAttrs.isEmpty()) {
      final IHCTable<?> aAttrTable = getStyler().createTable(new HCCol(170), HCCol.star());
      aAttrTable
          .addHeaderRow()
          .addCells(
              EText.HEADER_NAME.getDisplayText(aDisplayLocale),
              EText.HEADER_VALUE.getDisplayText(aDisplayLocale));
      for (final Map.Entry<String, Object> aEntry : aCustomAttrs.entrySet()) {
        final String sName = aEntry.getKey();
        if (aHandledAttrs == null || !aHandledAttrs.contains(sName)) {
          final String sValue = String.valueOf(aEntry.getValue());
          aAttrTable.addBodyRow().addCells(sName, sValue);
        }
      }

      // Maybe all custom attributes where handled in
      // showCustomAttrsOfSelectedObject
      if (aAttrTable.hasBodyRows())
        aTable
            .createItemRow()
            .setLabel(EText.LABEL_ATTRIBUTES.getDisplayText(aDisplayLocale))
            .setCtrl(aAttrTable);
    }

    // Callback
    onShowSelectedObjectTableEnd(aTable, aSelectedObject, aDisplayLocale);
  }
  @Override
  @SuppressWarnings("null")
  protected void validateAndSaveInputParameters(
      @Nonnull final WebPageExecutionContext aWPEC,
      @Nullable final IUser aSelectedObject,
      @Nonnull final FormErrors aFormErrors,
      final boolean bEdit) {
    final HCNodeList aNodeList = aWPEC.getNodeList();
    final Locale aDisplayLocale = aWPEC.getDisplayLocale();
    final boolean bIsAdministrator = aSelectedObject != null && aSelectedObject.isAdministrator();
    final AccessManager aAccessMgr = AccessManager.getInstance();
    String sLoginName = aWPEC.getAttr(FIELD_LOGINNAME);
    final String sFirstName = aWPEC.getAttr(FIELD_FIRSTNAME);
    final String sLastName = aWPEC.getAttr(FIELD_LASTNAME);
    final String sEmailAddress = aWPEC.getAttr(FIELD_EMAILADDRESS);
    final String sPassword = aWPEC.getAttr(FIELD_PASSWORD);
    final String sPasswordConf = aWPEC.getAttr(FIELD_PASSWORD_CONFIRM);
    final boolean bEnabled =
        bIsAdministrator ? true : aWPEC.getCheckBoxAttr(FIELD_ENABLED, DEFAULT_ENABLED);
    final Collection<String> aUserGroupIDs =
        bIsAdministrator
            ? aAccessMgr.getAllUserGroupIDsWithAssignedUser(aSelectedObject.getID())
            : aWPEC.getAttrs(FIELD_USERGROUPS);

    if (useEmailAddressAsLoginName()) {
      sLoginName = sEmailAddress;
    } else {
      if (StringHelper.hasNoText(sLoginName))
        aFormErrors.addFieldError(
            FIELD_LOGINNAME, EText.ERROR_LOGINNAME_REQUIRED.getDisplayText(aDisplayLocale));
    }

    if (StringHelper.hasNoText(sLastName)) {
      if (isLastNameMandatory())
        aFormErrors.addFieldError(
            FIELD_LASTNAME, EText.ERROR_LASTNAME_REQUIRED.getDisplayText(aDisplayLocale));
    }

    if (StringHelper.hasNoText(sEmailAddress)) {
      if (isEmailMandatory())
        aFormErrors.addFieldError(
            FIELD_EMAILADDRESS, EText.ERROR_EMAIL_REQUIRED.getDisplayText(aDisplayLocale));
    } else if (!EmailAddressUtils.isValid(sEmailAddress))
      aFormErrors.addFieldError(
          FIELD_EMAILADDRESS, EText.ERROR_EMAIL_INVALID.getDisplayText(aDisplayLocale));
    else {
      final IUser aSameLoginUser = aAccessMgr.getUserOfLoginName(sEmailAddress);
      if (aSameLoginUser != null)
        if (!bEdit || !aSameLoginUser.equals(aSelectedObject))
          aFormErrors.addFieldError(
              FIELD_EMAILADDRESS, EText.ERROR_EMAIL_IN_USE.getDisplayText(aDisplayLocale));
    }

    if (!bEdit) {
      final List<String> aPasswordErrors =
          GlobalPasswordSettings.getPasswordConstraintList()
              .getInvalidPasswordDescriptions(sPassword, aDisplayLocale);
      for (final String sPasswordError : aPasswordErrors)
        aFormErrors.addFieldError(FIELD_PASSWORD, sPasswordError);
      if (!EqualsUtils.equals(sPassword, sPasswordConf))
        aFormErrors.addFieldError(
            FIELD_PASSWORD_CONFIRM,
            EText.ERROR_PASSWORDS_DONT_MATCH.getDisplayText(aDisplayLocale));
    }

    if (ContainerHelper.isEmpty(aUserGroupIDs))
      aFormErrors.addFieldError(
          FIELD_USERGROUPS, EText.ERROR_NO_USERGROUP.getDisplayText(aDisplayLocale));
    else if (!aAccessMgr.containsAllUserGroupsWithID(aUserGroupIDs))
      aFormErrors.addFieldError(
          FIELD_USERGROUPS, EText.ERROR_INVALID_USERGROUPS.getDisplayText(aDisplayLocale));

    // Call custom method
    final Map<String, String> aCustomAttrMap =
        validateCustomParameters(aWPEC, aSelectedObject, aFormErrors, bEdit);

    if (aFormErrors.isEmpty()) {
      // All fields are valid -> save
      if (bEdit) {
        final String sUserID = aSelectedObject.getID();

        final Map<String, Object> aAttrMap = aSelectedObject.getAllAttributes();
        if (aCustomAttrMap != null) aAttrMap.putAll(aCustomAttrMap);

        // We're editing an existing object
        aAccessMgr.setUserData(
            sUserID,
            sLoginName,
            sEmailAddress,
            sFirstName,
            sLastName,
            m_aDefaultUserLocale,
            aAttrMap,
            !bEnabled);
        aNodeList.addChild(
            getStyler().createSuccessBox(EText.SUCCESS_EDIT.getDisplayText(aDisplayLocale)));

        // assign to the matching user groups
        final Collection<String> aPrevUserGroupIDs =
            aAccessMgr.getAllUserGroupIDsWithAssignedUser(sUserID);
        // Create all missing assignments
        final Set<String> aUserGroupsToBeAssigned =
            ContainerHelper.getDifference(aUserGroupIDs, aPrevUserGroupIDs);
        for (final String sUserGroupID : aUserGroupsToBeAssigned)
          aAccessMgr.assignUserToUserGroup(sUserGroupID, sUserID);

        // Delete all old assignments
        final Set<String> aUserGroupsToBeUnassigned =
            ContainerHelper.getDifference(aPrevUserGroupIDs, aUserGroupIDs);
        for (final String sUserGroupID : aUserGroupsToBeUnassigned)
          aAccessMgr.unassignUserFromUserGroup(sUserGroupID, sUserID);

      } else {
        // We're creating a new object
        final IUser aNewUser =
            aAccessMgr.createNewUser(
                sLoginName,
                sEmailAddress,
                sPassword,
                sFirstName,
                sLastName,
                m_aDefaultUserLocale,
                aCustomAttrMap,
                !bEnabled);
        if (aNewUser != null) {
          aNodeList.addChild(
              getStyler().createSuccessBox(EText.SUCCESS_CREATE.getDisplayText(aDisplayLocale)));

          // assign to the matching internal user groups
          for (final String sUserGroupID : aUserGroupIDs)
            aAccessMgr.assignUserToUserGroup(sUserGroupID, aNewUser.getID());
        } else
          aNodeList.addChild(
              getStyler().createErrorBox(EText.FAILURE_CREATE.getDisplayText(aDisplayLocale)));
      }
    }
  }
  @Override
  protected final void fillContent(@Nonnull final WPECTYPE aWPEC) {
    final HCNodeList aNodeList = aWPEC.getNodeList();

    // Get the selected object
    final DATATYPE aSelectedObject = getSelectedObject(aWPEC, getSelectedObjectID(aWPEC));

    final boolean bIsEditAllowed = isEditAllowed(aWPEC, aSelectedObject);
    boolean bShowList = true;
    final String sAction = aWPEC.getAction();
    EWebPageFormAction eFormAction = null;
    if (ACTION_VIEW.equals(sAction) && aSelectedObject != null)
      eFormAction = EWebPageFormAction.VIEW;
    else if (ACTION_CREATE.equals(sAction)) eFormAction = EWebPageFormAction.CREATE;
    else if (ACTION_EDIT.equals(sAction) && bIsEditAllowed && aSelectedObject != null)
      eFormAction = EWebPageFormAction.EDIT;
    else if (ACTION_COPY.equals(sAction) && aSelectedObject != null)
      eFormAction = EWebPageFormAction.COPY;
    else if (ACTION_DELETE.equals(sAction) && aSelectedObject != null)
      eFormAction = EWebPageFormAction.DELETE;
    else if (ACTION_UNDELETE.equals(sAction) && aSelectedObject != null)
      eFormAction = EWebPageFormAction.UNDELETE;

    // Try to lock object
    if (beforeProcessing(aWPEC, aSelectedObject, eFormAction).isContinue()) {
      if (eFormAction == EWebPageFormAction.VIEW) {
        // Valid object found - show details
        handleViewObject(aWPEC, aSelectedObject, bIsEditAllowed);

        bShowList = false;
      } else {
        if (eFormAction == EWebPageFormAction.CREATE
            || eFormAction == EWebPageFormAction.EDIT
            || eFormAction == EWebPageFormAction.COPY) {
          final boolean bIsEdit = eFormAction == EWebPageFormAction.EDIT;
          final boolean bIsCopy = eFormAction == EWebPageFormAction.COPY;

          // Create or edit a client
          final FormErrors aFormErrors = new FormErrors();
          boolean bShowInputForm = true;

          if (aWPEC.hasSubAction(CHCParam.ACTION_SAVE)) {
            // try to save
            validateAndSaveInputParameters(aWPEC, aSelectedObject, aFormErrors, bIsEdit);
            if (aFormErrors.isEmpty()) {
              // Save successful
              bShowInputForm = false;

              // Remove an optionally stored state
              FormStateManager.getInstance()
                  .deleteFormState(aWPEC.getAttributeAsString(FIELD_FLOW_ID));
            } else {
              // Show: changes could not be saved...
              aNodeList.addChild(getStyler().createIncorrectInputBox(aWPEC));
            }
          }

          if (bShowInputForm) {
            // Show the input form. Either for the first time or because of form
            // errors a n-th time
            bShowList = false;
            final HCForm aForm =
                isFileUploadForm(aWPEC) ? createFormFileUploadSelf(aWPEC) : createFormSelf(aWPEC);
            aForm.setID(INPUT_FORM_ID);
            aNodeList.addChild(aForm);

            // The unique form ID, that allows to identify on "transaction"
            // -> Used only for "form state remembering"
            aForm.addChild(
                new HCHiddenField(
                    new RequestField(FIELD_FLOW_ID, GlobalIDFactory.getNewStringID())));

            modifyFormBeforeShowInputForm(aWPEC, aForm);

            // Is there as saved state to use?
            final String sRestoreFlowID = aWPEC.getAttributeAsString(FIELD_RESTORE_FLOW_ID);
            if (sRestoreFlowID != null) {
              final FormState aSavedState =
                  FormStateManager.getInstance().getFormStateOfID(sRestoreFlowID);
              if (aSavedState != null) {
                // Restore all form values
                aForm.addChild(
                    new HCScriptOnDocumentReady(
                        JSFormHelper.setAllFormValues(
                            INPUT_FORM_ID, aSavedState.getAsAssocArray())));
              }
            }

            // Show the main input form
            showInputForm(aWPEC, aSelectedObject, aForm, bIsEdit, bIsCopy, aFormErrors);

            // Toolbar on bottom
            if (bIsEdit) {
              if (showEditToolbar(aWPEC, aSelectedObject))
                aForm.addChild(createEditToolbar(aWPEC, aForm, aSelectedObject));
            } else {
              if (showCreateToolbar(aWPEC, aSelectedObject))
                aForm.addChild(createCreateToolbar(aWPEC, aForm, aSelectedObject));
            }
          }
        } else if (eFormAction == EWebPageFormAction.DELETE) {
          bShowList = handleDeleteAction(aWPEC, aSelectedObject);
        } else if (eFormAction == EWebPageFormAction.UNDELETE) {
          bShowList = handleUndeleteAction(aWPEC, aSelectedObject);
        } else {
          // Other proprietary actions
          bShowList = handleCustomActions(aWPEC, aSelectedObject);
        }
      }
    }

    if (bShowList) {
      showListOfExistingObjects(aWPEC);
    }

    // Call after everything
    afterProcessing(aWPEC, aSelectedObject, eFormAction);
  }