diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/ConfirmDiscardDialog.java b/ccm-core/src/main/java/org/libreccm/admin/ui/ConfirmDiscardDialog.java
index 8183d7926..b6f145156 100644
--- a/ccm-core/src/main/java/org/libreccm/admin/ui/ConfirmDiscardDialog.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/ConfirmDiscardDialog.java
@@ -45,6 +45,8 @@ public class ConfirmDiscardDialog extends Window {
+ "used with itself.");
}
+ setCaption(message);
+
final Label label = new Label(message);
final ResourceBundle bundle = ResourceBundle
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UserEditor.java b/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UserEditor.java
index 07a4bf09a..bb57c9cf1 100644
--- a/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UserEditor.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UserEditor.java
@@ -20,13 +20,14 @@ package org.libreccm.admin.ui.usersgroupsroles;
import com.arsdigita.ui.admin.AdminUiConstants;
+import com.vaadin.data.HasValue;
import com.vaadin.data.provider.AbstractDataProvider;
import com.vaadin.data.provider.Query;
+import com.vaadin.server.UserError;
import com.vaadin.ui.Button;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.HorizontalLayout;
-import com.vaadin.ui.ItemCaptionGenerator;
import com.vaadin.ui.Panel;
import com.vaadin.ui.PasswordField;
import com.vaadin.ui.RadioButtonGroup;
@@ -34,16 +35,24 @@ import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.libreccm.admin.ui.ConfirmDiscardDialog;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.core.EmailAddress;
import org.libreccm.core.UnexpectedErrorException;
+import org.libreccm.security.ChallengeManager;
import org.libreccm.security.User;
import org.libreccm.security.UserManager;
import org.libreccm.security.UserRepository;
import java.util.Arrays;
import java.util.ResourceBundle;
+import java.util.regex.Pattern;
import java.util.stream.Stream;
+import javax.mail.MessagingException;
+
/**
*
* @author Jens Pelzetter
@@ -52,17 +61,23 @@ public class UserEditor extends Window {
private static final long serialVersionUID = 7024424532574023431L;
+ private static final Logger LOGGER = LogManager.getLogger(UserEditor.class);
+
private enum PasswordOptions {
+ DO_NOTHING,
GENERATE_AND_SEND,
- SET
+ SET,
}
+ private final UsersGroupsRoles usersGroupsRoles;
private final User user;
private final UserRepository userRepo;
private final UserManager userManager;
+ private boolean dataHasChanged = false;
+
private TextField userName;
private TextField familyName;
private TextField givenName;
@@ -73,11 +88,13 @@ public class UserEditor extends Window {
private CheckBox passwordResetRequired;
private CheckBox banned;
- public UserEditor(final UserRepository userRepo,
+ public UserEditor(final UsersGroupsRoles usersGroupsRoles,
+ final UserRepository userRepo,
final UserManager userManager) {
-
+
super("Create new user");
-
+
+ this.usersGroupsRoles = usersGroupsRoles;
user = null;
this.userRepo = userRepo;
this.userManager = userManager;
@@ -86,12 +103,14 @@ public class UserEditor extends Window {
}
public UserEditor(final User user,
+ final UsersGroupsRoles usersGroupsRoles,
final UserRepository userRepo,
final UserManager userManager) {
super(String.format("Edit user %s", user.getName()));
-
+
this.user = user;
+ this.usersGroupsRoles = usersGroupsRoles;
this.userRepo = userRepo;
this.userManager = userManager;
@@ -103,20 +122,27 @@ public class UserEditor extends Window {
final ResourceBundle bundle = ResourceBundle
.getBundle(AdminUiConstants.ADMIN_BUNDLE,
UI.getCurrent().getLocale());
-
+
+ final DataHasChangedListener dataHasChangedListener
+ = new DataHasChangedListener();
+
userName = new TextField(bundle
.getString("ui.admin.user_edit.username.label"));
userName.setRequiredIndicatorVisible(true);
+ userName.addValueChangeListener(dataHasChangedListener);
familyName = new TextField(bundle
.getString("ui.admin.user_edit.familyname.label"));
+ familyName.addValueChangeListener(dataHasChangedListener);
givenName = new TextField(bundle
.getString("ui.admin.user_edit.givenname.label"));
+ givenName.addValueChangeListener(dataHasChangedListener);
emailAddress = new TextField(bundle
.getString("ui.admin.user_edit.emailAddress.label"));
emailAddress.setRequiredIndicatorVisible(true);
+ givenName.addValueChangeListener(dataHasChangedListener);
passwordOptions = new RadioButtonGroup(
bundle.getString("ui.admin.user_edit.password_options.label"),
@@ -131,13 +157,23 @@ public class UserEditor extends Window {
@Override
public int size(final Query query) {
- return PasswordOptions.values().length;
+ if (user == null) {
+ return PasswordOptions.values().length - 1;
+ } else {
+ return PasswordOptions.values().length;
+ }
}
@Override
public Stream fetch(
final Query query) {
- return Arrays.stream(PasswordOptions.values());
+ if (user == null) {
+ return Arrays
+ .stream(PasswordOptions.values())
+ .filter(option -> option != PasswordOptions.DO_NOTHING);
+ } else {
+ return Arrays.stream(PasswordOptions.values());
+ }
}
});
@@ -150,6 +186,9 @@ public class UserEditor extends Window {
case SET:
return bundle.getString(
"ui.admin.user_edit.password_options.set");
+ case DO_NOTHING:
+ return bundle.getString(
+ "ui.admin.user_edit.password_options.do_nothing");
default:
throw new UnexpectedErrorException(String.format(
"Unexpected value '%s' for password options.",
@@ -160,6 +199,7 @@ public class UserEditor extends Window {
password = new PasswordField(bundle
.getString("ui.admin.user_edit.password.label"));
password.setRequiredIndicatorVisible(true);
+ password.addValueChangeListener(dataHasChangedListener);
passwordConfirmation = new PasswordField(bundle
.getString("ui.admin.user_set_password_confirm.label"));
@@ -179,6 +219,12 @@ public class UserEditor extends Window {
passwordConfirmation.setEnabled(true);
passwordConfirmation.setVisible(true);
break;
+ case DO_NOTHING:
+ password.setEnabled(false);
+ password.setVisible(false);
+ passwordConfirmation.setEnabled(false);
+ passwordConfirmation.setVisible(false);
+ break;
default:
throw new UnexpectedErrorException(String.format(
"Unexpected value '%s' for password options.",
@@ -188,11 +234,16 @@ public class UserEditor extends Window {
passwordOptions.setValue(PasswordOptions.GENERATE_AND_SEND);
+ final CheckBoxHasChangedListener checkBoxHasChangedListener
+ = new CheckBoxHasChangedListener();
+
passwordResetRequired = new CheckBox(bundle
.getString("ui.admin.user_edit.password_reset_required.label"));
+ passwordResetRequired.addValueChangeListener(checkBoxHasChangedListener);
banned = new CheckBox(bundle
.getString("ui.admin.user_edit.banned.label"));
+ banned.addValueChangeListener(checkBoxHasChangedListener);
if (user == null) {
banned.setVisible(false);
@@ -206,6 +257,7 @@ public class UserEditor extends Window {
} else {
submit.setCaption(bundle.getString("ui.admin.save"));
}
+ submit.addClickListener(event -> saveUser());
final Button cancel = new Button(bundle.getString("ui.admin.cancel"));
cancel.addClickListener(event -> close());
@@ -234,16 +286,182 @@ public class UserEditor extends Window {
}
setContent(panel);
+
+ if (user != null) {
+ userName.setValue(user.getName());
+ givenName.setValue(user.getGivenName());
+ familyName.setValue(user.getFamilyName());
+ emailAddress.setValue(user.getPrimaryEmailAddress().getAddress());
+ passwordResetRequired.setValue(user.isPasswordResetRequired());
+ banned.setValue(user.isBanned());
+ passwordOptions.setValue(PasswordOptions.DO_NOTHING);
+ }
}
-
+
@Override
public void close() {
-
- final ConfirmDiscardDialog dialog = new ConfirmDiscardDialog(
+
+ if (dataHasChanged) {
+ final ConfirmDiscardDialog dialog = new ConfirmDiscardDialog(
this, "Are you sure to discard the changes made this user?");
dialog.setModal(true);
UI.getCurrent().addWindow(dialog);
-
+ } else {
+ super.close();
+ }
+ }
+
+ protected void saveUser() {
+
+ final ResourceBundle bundle = ResourceBundle
+ .getBundle(AdminUiConstants.ADMIN_BUNDLE,
+ UI.getCurrent().getLocale());
+
+ boolean valid = true;
+
+ if (userName.getValue() == null
+ || userName.getValue().trim().isEmpty()) {
+ userName.setComponentError(new UserError(bundle
+ .getString("ui.admin.user_edit.username.error.not_empty")));
+ valid = false;
+ }
+ if (emailAddress.getValue() == null
+ || emailAddress.getValue().trim().isEmpty()) {
+ emailAddress.setComponentError(new UserError(bundle
+ .getString("ui.admin.user.email_form.address.not_empty")));
+ valid = false;
+ }
+
+ if ((user == null || !user.getName().equals(userName.getValue()))
+ && userRepo.isNameInUse(userName.getValue())) {
+
+ userName.setComponentError(new UserError(bundle
+ .getString("ui.admin.new_user_form.error"
+ + ".username_already_in_use")));
+ valid = false;
+ } else {
+ userName.setComponentError(null);
+ }
+
+ if ((user == null
+ || !user.getPrimaryEmailAddress().getAddress().equals(
+ emailAddress.getValue()))
+ && userRepo.isEmailAddressInUse(emailAddress.getValue())) {
+
+ emailAddress.setComponentError(new UserError(bundle
+ .getString("ui.admin.new_user_form.error.email_already_in_use")));
+ valid = false;
+ } else {
+ if (!Pattern.matches(
+ "^[^@<>\"\\t ]+@[^@<>\".\\t]+([.][^@<>\".\\n ]+)+$",
+ emailAddress.getValue().trim())) {
+ emailAddress.setComponentError(new UserError(bundle
+ .getString("ui.admin.user_form.email_malformed")));
+ valid = false;
+ } else {
+ emailAddress.setComponentError(null);
+ }
+ }
+
+ if (passwordOptions.getValue() == PasswordOptions.SET) {
+ if (password.getValue() == null
+ || password.getValue().trim().isEmpty()) {
+ password.setComponentError(new UserError(bundle
+ .getString(
+ "ui.admin.set_password.new_password.error.not_empty")));
+ }
+
+ if (!password.getValue().equals(passwordConfirmation.getValue())) {
+ passwordConfirmation.setComponentError(new UserError(bundle
+ .getString("ui.admin.user_set_password.error.do_not_match")));
+ }
+ }
+
+ if (!valid) {
+ return;
+ }
+
+ final User currentUser;
+ if (user == null) {
+
+ final User newUser = userManager.createUser(givenName.getValue(),
+ familyName.getValue(),
+ userName.getValue(),
+ emailAddress.getValue(),
+ passwordConfirmation
+ .getValue());
+
+ newUser.setPasswordResetRequired(passwordResetRequired.getValue());
+ newUser.setBanned(banned.getValue());
+ currentUser = newUser;
+
+ userRepo.save(newUser);
+ } else {
+ user.setName(userName.getValue().trim());
+ user.setGivenName(givenName.getValue().trim());
+ user.setFamilyName(familyName.getValue().trim());
+ final EmailAddress email = user.getPrimaryEmailAddress();
+ if (!email.getAddress().equals(emailAddress.getValue())) {
+ email.setAddress(emailAddress.getValue());
+ }
+ user.setPasswordResetRequired(passwordResetRequired.getValue());
+ user.setBanned(banned.getValue());
+
+ userRepo.save(user);
+ currentUser = user;
+ }
+
+ switch (passwordOptions.getValue()) {
+ case GENERATE_AND_SEND: {
+ userManager.updatePassword(currentUser, null);
+ final ChallengeManager challengeManager = CdiUtil
+ .createCdiUtil()
+ .findBean(ChallengeManager.class);
+ try {
+ challengeManager.sendPasswordRecover(currentUser);
+ } catch (MessagingException ex) {
+ setComponentError(new UserError(bundle
+ .getString("ui.admin.user_form"
+ + ".failed_to_send_password_challenge")));
+ LOGGER.error(
+ "Failed to send password challenge.",
+ ex);
+ }
+ break;
+ }
+ case SET:
+ userManager.updatePassword(currentUser,
+ passwordConfirmation.getValue());
+ break;
+ }
+
+ dataHasChanged = false;
+ usersGroupsRoles.refreshUsers();
+ close();
+ }
+
+ private class DataHasChangedListener implements
+ HasValue.ValueChangeListener {
+
+ private static final long serialVersionUID = -4698658552890778877L;
+
+ @Override
+ public void valueChange(final HasValue.ValueChangeEvent event) {
+ dataHasChanged = true;
+ }
+
+ }
+
+ private class CheckBoxHasChangedListener implements
+ HasValue.ValueChangeListener {
+
+ private static final long serialVersionUID = 1986372149566327203L;
+
+ @Override
+ public void valueChange(final HasValue.ValueChangeEvent event) {
+ dataHasChanged = true;
+ }
+
}
}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UsersGroupsRoles.java b/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UsersGroupsRoles.java
index 10d985e85..76fe06860 100644
--- a/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UsersGroupsRoles.java
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/usersgroupsroles/UsersGroupsRoles.java
@@ -20,6 +20,7 @@ package org.libreccm.admin.ui.usersgroupsroles;
import com.arsdigita.ui.admin.AdminUiConstants;
+import com.vaadin.icons.VaadinIcons;
import com.vaadin.ui.Button;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.Grid;
@@ -27,12 +28,14 @@ import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
+import com.vaadin.ui.Window;
import com.vaadin.ui.components.grid.HeaderCell;
import com.vaadin.ui.components.grid.HeaderRow;
import com.vaadin.ui.renderers.ButtonRenderer;
import com.vaadin.ui.themes.ValoTheme;
import org.libreccm.admin.ui.AdminView;
import org.libreccm.security.User;
+import org.libreccm.security.UserRepository;
import java.util.ResourceBundle;
@@ -61,6 +64,7 @@ public class UsersGroupsRoles extends CustomComponent {
private final Grid usersTable;
private final TextField userNameFilter;
private final Button clearFiltersButton;
+ private final Button createUserButton;
private UsersTableDataProvider usersTableDataProvider;
@@ -118,23 +122,13 @@ public class UsersGroupsRoles extends CustomComponent {
new ButtonRenderer<>(event -> {
final UserEditor editor = new UserEditor(
event.getItem(),
+ this,
view.getUserRepository(),
view.getUserManager());
editor.center();
UI.getCurrent().addWindow(editor);
}))
.setId(COL_EDIT);
- usersTable
- .addColumn(user -> bundle.getString("ui.admin.users.table.delete"),
- new ButtonRenderer<>(event -> {
- final UserEditor editor = new UserEditor(
- event.getItem(),
- view.getUserRepository(),
- view.getUserManager());
- editor.center();
- UI.getCurrent().addWindow(editor);
- }))
- .setId(COL_DELETE);
final HeaderRow filterRow = usersTable.appendHeaderRow();
final HeaderCell userNameFilterCell = filterRow.getCell(COL_USER_NAME);
@@ -161,8 +155,20 @@ public class UsersGroupsRoles extends CustomComponent {
// usersTableDataProvider.setUserNameFilter(null);
userNameFilter.setValue("");
});
+ createUserButton = new Button("New User");
+ createUserButton.addStyleName(ValoTheme.BUTTON_TINY);
+ createUserButton.setIcon(VaadinIcons.PLUS);
+ createUserButton.addClickListener(event -> {
+ final UserEditor userEditor = new UserEditor(
+ this,
+ view.getUserRepository(),
+ view.getUserManager());
+ userEditor.center();
+ UI.getCurrent().addWindow(userEditor);
+ });
final HorizontalLayout actionsLayout = new HorizontalLayout(
- clearFiltersButton);
+ clearFiltersButton,
+ createUserButton);
actionsCell.setComponent(actionsLayout);
tabSheet.addTab(usersTable, "Users");
@@ -214,5 +220,9 @@ public class UsersGroupsRoles extends CustomComponent {
this.usersTableDataProvider = dataProvider;
usersTable.setDataProvider(dataProvider);
}
-
+
+ protected void refreshUsers() {
+ usersTableDataProvider.refreshAll();
+ }
+
}
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties
index 0378c3791..3933e2b3c 100644
--- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties
@@ -225,7 +225,7 @@ ui.admin.new_user_form.password_options.set_password.password.label=Password
ui.admin.new_user_form.password_options.set_password.password_confirmation.label=Confirm password
ui.admin.new_user_form.password_options.send_password.label=Send password
ui.admin.new_user_form.error.username_already_in_use=The provided user name is already in use.
-ui.admin.new_user_form.error.email_already_in_use=The provided email address is already assigned with an user account.
+ui.admin.new_user_form.error.email_already_in_use=The provided email address is already associated with an user account.
ui.admin.new_user_form.error.password_do_not_match=Passwords do not match.
ui.admin.new_user_form.error.failed_to_send_password=Failed to send password to new user.
ui.admin.new_user_form.password_options.set_password=Set password
@@ -574,3 +574,6 @@ ui.admin.user_edit.password_options.generate_and_send=Generate and send to user
ui.admin.user_edit.password_options.set=Set password
ui.admin.yes=Yes
ui.admin.no=No
+ui.admin.user_form.email_malformed=This email address is malformed.
+ui.admin.user_edit.password_options.do_nothing=Do not change password
+ui.admin.user_form.failed_to_send_password_challenge=Failed to send password challenge to user.
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties
index 03969e056..f7ac25f28 100644
--- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties
@@ -578,3 +578,6 @@ ui.admin.user_edit.password_options.generate_and_send=Generieren und an Benutzer
ui.admin.user_edit.password_options.set=Passwort setzen
ui.admin.yes=Ja
ui.admin.no=Nein
+ui.admin.user_form.email_malformed=E-Mail Adresse ist fehlerhaft.
+ui.admin.user_edit.password_options.do_nothing=Password nicht ver\u00e4ndern
+ui.admin.user_form.failed_to_send_password_challenge=Beim Senden des Passwortes ist ein Fehler aufgetreten.
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties
index 2ced8d20a..d1b045161 100755
--- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties
@@ -198,7 +198,7 @@ ui.admin.new_user_form.password_options.set_password.password.label=Password
ui.admin.new_user_form.password_options.set_password.password_confirmation.label=Confirm password
ui.admin.new_user_form.password_options.send_password.label=Send password
ui.admin.new_user_form.error.username_already_in_use=The provided user name is already in use.
-ui.admin.new_user_form.error.email_already_in_use=The provided email address is already assigned with an user account.
+ui.admin.new_user_form.error.email_already_in_use=The provided email address is already associated with an user account.
ui.admin.new_user_form.error.password_do_not_match=Passwords do not match.
ui.admin.new_user_form.error.failed_to_send_password=Failed to send password to new user.
ui.admin.new_user_form.password_options.set_password=Set password
@@ -571,3 +571,6 @@ ui.admin.user_edit.password_options.generate_and_send=Generate and send to user
ui.admin.user_edit.password_options.set=Set password
ui.admin.yes=Yes
ui.admin.no=No
+ui.admin.user_form.email_malformed=This email address is malformed.
+ui.admin.user_edit.password_options.do_nothing=Do not change password
+ui.admin.user_form.failed_to_send_password_challenge=Failed to send password challenge to user.
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties
index 18deb5867..3539f8352 100755
--- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties
@@ -189,7 +189,7 @@ ui.admin.new_user_form.password_options.set_password.password.label=Password
ui.admin.new_user_form.password_options.set_password.password_confirmation.label=Confirm password
ui.admin.new_user_form.password_options.send_password.label=Send password
ui.admin.new_user_form.error.username_already_in_use=The provided user name is already in use.
-ui.admin.new_user_form.error.email_already_in_use=The provided email address is already assigned with an user account.
+ui.admin.new_user_form.error.email_already_in_use=The provided email address is already associated with an user account.
ui.admin.new_user_form.error.password_do_not_match=Passwords do not match.
ui.admin.new_user_form.error.failed_to_send_password=Failed to send password to new user.
ui.admin.new_user_form.password_options.set_password=Set password
@@ -562,3 +562,6 @@ ui.admin.user_edit.password_options.generate_and_send=Generate and send to user
ui.admin.user_edit.password_options.set=Set password
ui.admin.yes=Yes
ui.admin.no=No
+ui.admin.user_form.email_malformed=This email address is malformed.
+ui.admin.user_edit.password_options.do_nothing=Do not change password
+ui.admin.user_form.failed_to_send_password_challenge=Failed to send password challenge to user.