Hi ,
While implementing new edit user screen , with role template
If i tried to save user with roles from the template , I’m getting an error as the user is still not yet saved , as trying to save instance with persist not cascade ,
but if i saved the user first , then editing and applying the template it works fine as the user is already saved ,
below is my current implementation
XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
caption="msg://editorCaption"
focusComponent="form"
messagesPack="com.vtss.vtower.web.screens.user">
<data>
<instance id="vTowerUserDc"
class="com.vtss.vtower.entity.user.VTowerUser"
view="vtower-user-base-edit">
<loader/>
<collection id="userRolesDc" property="userRoles"/>
<collection id="assignableOrganizationsDc" property="assignableOrganizations"/>
</instance>
<collection id="groupsDc" class="com.haulmont.cuba.security.entity.Group" view="_minimal">
<loader id="groupsDl">
<query>
<![CDATA[select e from sec$Group e]]>
</query>
</loader>
</collection>
</data>
<dialogMode height="AUTO"
width="AUTO"
modal="true"
forceDialog="true"/>
<layout expand="scrollBox" spacing="true">
<scrollBox id="scrollBox" spacing="true">
<form id="form" dataContainer="vTowerUserDc">
<column width="250px">
<textField id="loginField" property="login" inputPrompt="msg://valid-email-address"/>
<textField id="nameField" required="true" requiredMessage="msg://name-required" property="name"/>
<textField id="positionField" property="position"/>
<lookupField id="groupField" property="group" required="true" requiredMessage="msg://group-required"
nullName="msg://null-option" optionsContainer="groupsDc"/>
<checkBox id="activeField" property="active"/>
</column>
<column width="250px" id="details-user-account">
<textField id="ipMaskField" property="ipMask"/>
<lookupField id="timeZoneField" property="timeZone"/>
<checkBox id="canBeAssignedToCountryField" property="canBeAssignedToCountry"/>
<checkBox id="isEmployeeField" property="isEmployee"/>
<checkBox id="isGlobalUserField" property="isGlobalUser"/>
</column>
</form>
<vbox spacing="true">
<lookupField id="rolesTemplateLKP" caption="msg://role-template" width="350px"/>
<buttonsPanel id="roleTemplateActionsPnl" enable="false">
<button id="applyTemplateBtn" caption="mainMsg://action.apply.role" stylename="primary"/>
<button id="revokeTemplateBtn" caption="mainMsg://action.revoke.role" stylename="friendly"/>
</buttonsPanel>
</vbox>
<hbox spacing="true" width="100%">
<groupBox width="50%" id="userRolesBox" caption="msg://user-roles">
<table id="userRolesTable" dataContainer="userRolesDc" width="100%">
<actions>
<action id="create" type="create"/>
<action id="remove" type="remove"/>
</actions>
<columns>
<column id="role"/>
<column id="roleName"/>
</columns>
<buttonsPanel>
<button action="userRolesTable.create" stylename="primary"/>
<button action="userRolesTable.remove" stylename="friendly"/>
</buttonsPanel>
</table>
</groupBox>
<groupBox width="50%" id="assignableOrganizationsBox"
caption="msg://assignable-organizations">
<table id="assignableOrganizationsTable" dataContainer="assignableOrganizationsDc" width="100%">
<actions>
<action id="create" type="create"/>
<action id="edit" type="edit"/>
<action id="remove" type="remove"/>
</actions>
<columns>
<column id="user"/>
<column id="organization"/>
</columns>
<buttonsPanel>
<button action="assignableOrganizationsTable.create" stylename="primary"/>
<button action="assignableOrganizationsTable.edit" stylename="friendly"/>
<button action="assignableOrganizationsTable.remove" stylename="danger"/>
</buttonsPanel>
</table>
</groupBox>
</hbox>
</scrollBox>
<hbox id="editActions" spacing="true">
<button action="windowCommitAndClose" stylename="primary"/>
<button action="windowClose" stylename="friendly"/>
</hbox>
</layout>
</window>
Controller
package com.vtss.vtower.web.screens.user;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.Notifications;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.model.CollectionPropertyContainer;
import com.haulmont.cuba.gui.model.InstanceContainer;
import com.haulmont.cuba.gui.screen.*;
import com.haulmont.cuba.security.entity.RoleType;
import com.haulmont.cuba.security.entity.User;
import com.haulmont.cuba.security.entity.UserRole;
import com.vtss.vtower.entity.approle.ApplicationRoleTemplate;
import com.vtss.vtower.entity.approle.ApplicationRoleType;
import com.vtss.vtower.entity.user.VTowerUser;
import com.vtss.vtower.service.ApplicationRoleTemplateService;
import com.vtss.vtower.web.screens.mixin.util.UIUtil;
import com.vtss.vtower.web.utils.Constants;
import javax.inject.Inject;
import java.util.*;
import java.util.regex.Pattern;
@UiController("vtower_VTowerUser.edit")
@UiDescriptor("v-tower-user-edit.xml")
@EditedEntityContainer("vTowerUserDc")
@LoadDataBeforeShow
public class VTowerUserEdit extends StandardEditor<VTowerUser> {
@Inject
private LookupField<String> timeZoneField;
@Inject
private LookupField<ApplicationRoleTemplate> rolesTemplateLKP;
@Inject
private Table<UserRole> userRolesTable;
@Inject
private ButtonsPanel roleTemplateActionsPnl;
@Inject
private ApplicationRoleTemplateService applicationRoleTemplateService;
@Inject
private CollectionPropertyContainer<UserRole> userRolesDc;
@Inject
private Metadata metadata;
@Inject
protected TimeZones timeZones;
private Map<String, Object> params;
@Inject
private Notifications notifications;
@Inject
private Messages messages;
@Inject
private MessageBundle messageBundle;
@Inject
private TextField<String> ipMaskField;
@Inject
private CheckBox canBeAssignedToCountryField;
@Inject
private GroupBoxLayout assignableOrganizationsBox;
@Inject
private CheckBox isGlobalUserField;
@Inject
private CheckBox isEmployeeField;
@Subscribe
public void onInit(InitEvent event) {
ScreenOptions options = event.getOptions();
if (options instanceof MapScreenOptions) {
params = ((MapScreenOptions) options).getParams();
if (params.containsKey(Constants.READ_ONLY_MODE)){
setReadOnly(Boolean.TRUE);
rolesTemplateLKP.setEnabled(Boolean.FALSE);
}
}
}
@Subscribe
public void onBeforeShow(BeforeShowEvent event) {
buildTimeZoneField();
buildRoleTemplateLkpField();
buildScreenComponents();
}
@Subscribe("rolesTemplateLKP")
public void onRolesTemplateLKPValueChange(HasValue.ValueChangeEvent event) {
if (Objects.nonNull(event)) {
if (Objects.nonNull(event.getValue())) {
roleTemplateActionsPnl.setEnabled(true);
} else {
roleTemplateActionsPnl.setEnabled(false);
}
}
}
@Subscribe("loginField")
public void onLoginFieldValueChange(HasValue.ValueChangeEvent<String> event) {
if (Objects.nonNull(event) && Objects.nonNull(event.getValue())) {
if (!Pattern.compile(Constants.EMAIL).matcher(event.getValue()).matches()) {
event.getComponent().setValue(null);
notifications.create().withCaption(messages.getMainMessage("msg-warning"))
.withDescription(messageBundle.getMessage("invalid-email-address"))
.withType(Notifications.NotificationType.TRAY)
.withPosition(Notifications.Position.BOTTOM_RIGHT)
.show();
} else {
getEditedEntity().setEmail(event.getValue());
}
}
}
@Subscribe(id = "userRolesDc", target = Target.DATA_CONTAINER)
public void onUserRolesDcItemChange(InstanceContainer.ItemChangeEvent<UserRole> event) {
if (Objects.nonNull(event) && Objects.nonNull(event.getItem())) {
if (userRolesDc.getItem().getRole().getDefaultRole() != null && userRolesDc.getItem().getRole().getDefaultRole()) {
userRolesTable.getAction("remove").setEnabled(false);
} else {
userRolesTable.getAction("remove").setEnabled(true);
}
}
}
@Subscribe("applyTemplateBtn")
public void onApplyTemplateBtnClick(Button.ClickEvent event) {
User user = getEditedEntity();
rolesTemplateLKP.getValue().getTemplateLines()
.forEach(applicationRoleTemplateLine -> {
if (!userRolesDc.getItems().stream()
.filter(userRole -> !userRole.getRoleName().equals("system-minimal"))
.anyMatch(userRole -> userRole.getRole().equals(applicationRoleTemplateLine.getRole()))) {
UserRole role = metadata.create(UserRole.class);
role.setUser(user);
role.setRole(applicationRoleTemplateLine.getRole());
userRolesDc.getMutableItems().add(role);
user.getUserRoles().add(role);
}
});
}
@Subscribe("revokeTemplateBtn")
public void onRevokeTemplateBtnClick(Button.ClickEvent event) {
rolesTemplateLKP.getValue().getTemplateLines()
.forEach(applicationRoleTemplateLine -> {
UserRole role = userRolesDc.getItems().stream()
.filter(userRole -> userRole.getRole().equals(applicationRoleTemplateLine.getRole()))
.findFirst().orElse(null);
if (role != null) {
userRolesDc.getMutableItems().remove(role);
}
});
}
private void buildTimeZoneField() {
Map<String, String> options = new TreeMap<>();
for (String id : TimeZone.getAvailableIDs()) {
TimeZone timeZone = TimeZone.getTimeZone(id);
options.put(timeZones.getDisplayNameLong(timeZone), id);
}
timeZoneField.setOptionsMap(options);
}
private void buildRoleTemplateLkpField() {
List<ApplicationRoleTemplate> roleTemplates = new ArrayList<>();
if (Objects.nonNull(params)) {
if (params.containsKey(Constants.LEGAL_ENTITY_PARAM)) {
roleTemplates = applicationRoleTemplateService
.getApplicationRoleTemplates(
(ApplicationRoleType) params.get(Constants.APPLICATION_ROLE_TYPE_PARAM)
,(UUID) params.get(Constants.LEGAL_ENTITY_PARAM),
Boolean.FALSE);
} else if (params.containsKey(Constants.ORGANIZATION_PARAM)) {
roleTemplates = applicationRoleTemplateService.getApplicationRoleTemplates(
(ApplicationRoleType) params.get(Constants.APPLICATION_ROLE_TYPE_PARAM),
(UUID) params.get(Constants.ORGANIZATION_PARAM),
Boolean.TRUE);
}else {
roleTemplates = applicationRoleTemplateService.getApplicationRoleTemplates(
(ApplicationRoleType) params.get(Constants.APPLICATION_ROLE_TYPE_PARAM));
}
} else {
roleTemplates = applicationRoleTemplateService.getApplicationRoleTemplates(null);
}
UIUtil.populateOptionsLookup(roleTemplates, rolesTemplateLKP);
}
private void buildScreenComponents() {
if (Objects.nonNull(params) && params.containsKey(Constants.OPENED_FROM_PARAM)) {
ipMaskField.setVisible(false);
canBeAssignedToCountryField.setVisible(false);
isGlobalUserField.setVisible(false);
isEmployeeField.setVisible(false);
timeZoneField.setVisible(false);
assignableOrganizationsBox.setVisible(false);
if (PersistenceHelper.isNew(getEditedEntity())) {
switch ((String) params.get(Constants.OPENED_FROM_PARAM)) {
case Constants.COUNTRY_PARAM:
getEditedEntity().setCanBeAssignedToCountry(true);
case Constants.GLOB_PARAM:
getEditedEntity().setIsGlobalUser(true);
}
}
} else {
VTowerUser user = (VTowerUser) AppBeans.get(UserSessionSource.class).getUserSession().getUser();
Boolean isSuperAdmin = user.getUserRoles().stream().anyMatch(userRole -> userRole.getRoleName().equals("system-full-access"));
if (!isSuperAdmin) {
if (!user.getCanBeAssignedToCountry()) {
canBeAssignedToCountryField.setEditable(false);
}
if (!user.getIsGlobalUser()) {
isGlobalUserField.setEditable(false);
}
}
}
}
}
I want to be able to save both the roles while saving the user , while the user is on the new state save behavior like CASCADE